1a4bab8ddc
CI / test (push) Successful in 16s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (push) Has been skipped
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (push) Has been skipped
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (push) Has been skipped
185 lines
5.2 KiB
Go
185 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestRunValidateURL_Usage(t *testing.T) {
|
|
var errBuf bytes.Buffer
|
|
origErr := errWriter
|
|
errWriter = &errBuf
|
|
defer func() { errWriter = origErr }()
|
|
|
|
code := runValidateURL(nil)
|
|
if code != 2 {
|
|
t.Errorf("expected exit code 2 for no args, got %d", code)
|
|
}
|
|
if !strings.Contains(errBuf.String(), "usage") {
|
|
t.Errorf("expected usage in stderr, got %q", errBuf.String())
|
|
}
|
|
|
|
errBuf.Reset()
|
|
code = runValidateURL([]string{"arg1", "arg2"})
|
|
if code != 2 {
|
|
t.Errorf("expected exit code 2 for too many args, got %d", code)
|
|
}
|
|
}
|
|
|
|
func TestValidateURL_MalformedURL(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
url string
|
|
wantMsg string
|
|
}{
|
|
{"empty", "", "must be https"},
|
|
{"http scheme", "http://example.com/", "must be https"},
|
|
{"ftp scheme", "ftp://example.com/", "must be https"},
|
|
{"no scheme", "example.com", "must be https"},
|
|
{"user info", "https://user:pass@example.com/", "user-info"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := validateURL(tc.url)
|
|
if err == nil {
|
|
t.Errorf("expected error for URL %q, got nil", tc.url)
|
|
return
|
|
}
|
|
if !strings.Contains(err.Error(), tc.wantMsg) {
|
|
t.Errorf("error %q does not contain %q", err.Error(), tc.wantMsg)
|
|
}
|
|
var ve *validateError
|
|
if !isValidateError(err, &ve) {
|
|
t.Fatalf("expected *validateError, got %T", err)
|
|
}
|
|
if ve.code != 2 {
|
|
t.Errorf("expected code 2, got %d", ve.code)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateURL_BlockedPrivateIP(t *testing.T) {
|
|
// localhost always resolves to 127.0.0.1 (loopback).
|
|
err := validateURL("https://localhost/")
|
|
if err == nil {
|
|
t.Skip("localhost did not resolve (network unavailable in test environment)")
|
|
}
|
|
var ve *validateError
|
|
if !isValidateError(err, &ve) {
|
|
t.Fatalf("expected *validateError, got %T: %v", err, err)
|
|
}
|
|
if ve.code != 1 && ve.code != 2 {
|
|
t.Errorf("expected code 1 (blocked) or 2 (dns fail), got %d: %s", ve.code, ve.message)
|
|
}
|
|
// If it resolved (code 1), the message must say "blocked".
|
|
if ve.code == 1 && !strings.Contains(ve.message, "blocked") {
|
|
t.Errorf("expected 'blocked' in message, got %q", ve.message)
|
|
}
|
|
}
|
|
|
|
func TestValidateURL_ExitCodes(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
url string
|
|
wantCode int
|
|
}{
|
|
{"http scheme", "http://example.com/", 2},
|
|
{"no scheme", "example.com", 2},
|
|
{"user info", "https://admin:secret@example.com/", 2},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := validateURL(tc.url)
|
|
if err == nil {
|
|
t.Fatalf("expected error for %q", tc.url)
|
|
}
|
|
var ve *validateError
|
|
if !isValidateError(err, &ve) {
|
|
t.Fatalf("expected *validateError, got %T", err)
|
|
}
|
|
if ve.code != tc.wantCode {
|
|
t.Errorf("code = %d, want %d (url=%q, msg=%s)", ve.code, tc.wantCode, tc.url, ve.message)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRunValidateURL_WithCapture(t *testing.T) {
|
|
var outBuf, errBuf bytes.Buffer
|
|
origOut, origErr := outWriter, errWriter
|
|
outWriter = &outBuf
|
|
errWriter = &errBuf
|
|
defer func() {
|
|
outWriter = origOut
|
|
errWriter = origErr
|
|
}()
|
|
|
|
// http:// scheme should fail with code 2.
|
|
code := runValidateURL([]string{"http://example.com/"})
|
|
if code != 2 {
|
|
t.Errorf("expected code 2 for http:// URL, got %d", code)
|
|
}
|
|
if !strings.Contains(errBuf.String(), "must be https") {
|
|
t.Errorf("expected error about https in stderr, got %q", errBuf.String())
|
|
}
|
|
}
|
|
|
|
// TestIsValidateError_Nil confirms that isValidateError returns false for a nil error.
|
|
func TestIsValidateError_Nil(t *testing.T) {
|
|
var ve *validateError
|
|
if isValidateError(nil, &ve) {
|
|
t.Error("isValidateError(nil, ...) should return false")
|
|
}
|
|
}
|
|
|
|
// TestValidateURL_EmptyHost confirms that a URL with no hostname returns a code-2 error.
|
|
func TestValidateURL_EmptyHost(t *testing.T) {
|
|
// "https://" parses fine but has no hostname.
|
|
err := validateURL("https://")
|
|
if err == nil {
|
|
t.Fatal("expected error for URL with no host, got nil")
|
|
}
|
|
var ve *validateError
|
|
if !isValidateError(err, &ve) {
|
|
t.Fatalf("expected *validateError, got %T: %v", err, err)
|
|
}
|
|
if ve.code != 2 {
|
|
t.Errorf("expected code 2, got %d (msg=%s)", ve.code, ve.message)
|
|
}
|
|
if !strings.Contains(ve.message, "no host") {
|
|
t.Errorf("expected 'no host' in error message, got %q", ve.message)
|
|
}
|
|
}
|
|
|
|
// TestRunValidateURL_Success confirms that a resolvable public URL prints "OK" and returns 0.
|
|
// This test requires external DNS; it is skipped in environments without network access.
|
|
func TestRunValidateURL_Success(t *testing.T) {
|
|
// Pre-check: validate that DNS is available before exercising the success path.
|
|
err := validateURL("https://example.com/")
|
|
if err != nil {
|
|
t.Skipf("skipping success-path test: DNS unavailable or example.com blocked (%v)", err)
|
|
}
|
|
|
|
var outBuf, errBuf bytes.Buffer
|
|
origOut, origErr := outWriter, errWriter
|
|
outWriter = &outBuf
|
|
errWriter = &errBuf
|
|
defer func() {
|
|
outWriter = origOut
|
|
errWriter = origErr
|
|
}()
|
|
|
|
code := runValidateURL([]string{"https://example.com/"})
|
|
if code != 0 {
|
|
t.Errorf("expected exit code 0 for safe URL, got %d (stderr: %s)", code, errBuf.String())
|
|
}
|
|
if !strings.Contains(outBuf.String(), "OK:") {
|
|
t.Errorf("expected 'OK:' in stdout, got %q", outBuf.String())
|
|
}
|
|
if errBuf.Len() != 0 {
|
|
t.Errorf("expected no stderr for safe URL, got %q", errBuf.String())
|
|
}
|
|
}
|