Coverage priorities 1-5: test pure functions, extract interfaces, mock-based handler tests

- Priority 1: Test NewSendEmailTask + NewSendPushTask (5 tests)
- Priority 2: Test customHTTPErrorHandler — all 15+ branches (21 tests)
- Priority 3: Extract Enqueuer interface + payload builders in worker pkg (5 tests)
- Priority 4: Extract ClassifyFile/ComputeRelPath in migrate-encrypt (6 tests)
- Priority 5: Define Handler interfaces, refactor to accept them, mock-based tests (14 tests)
- Fix .gitignore: /worker instead of worker to stop ignoring internal/worker/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-01 20:30:09 -05:00
parent 00fd674b56
commit bec880886b
83 changed files with 19569 additions and 730 deletions

View File

@@ -1,9 +1,12 @@
package validator
import (
"fmt"
"testing"
govalidator "github.com/go-playground/validator/v10"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestValidatePasswordComplexity(t *testing.T) {
@@ -113,3 +116,118 @@ func TestFormatMessagePasswordComplexity(t *testing.T) {
t.Errorf("expected tag 'password_complexity', got %q", field.Tag)
}
}
func TestPasswordComplexity_AdditionalCases(t *testing.T) {
cv := NewCustomValidator()
type request struct {
Password string `json:"password" validate:"required,min=8,password_complexity"`
}
tests := []struct {
name string
pw string
valid bool
}{
{"no uppercase no digit", "password", false},
{"no lowercase", "PASSWORD1", false},
{"no digit", "Password", false},
{"too short", "Pass1", false},
{"valid standard", "Password1", true},
{"valid with special chars", "P@ssw0rd", true},
{"spaces with complexity", "Pass 1234", true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
r := request{Password: tc.pw}
err := cv.Validate(r)
if tc.valid {
assert.NoError(t, err, "expected %q to be valid", tc.pw)
} else {
assert.Error(t, err, "expected %q to be invalid", tc.pw)
}
})
}
}
func TestFormatValidationErrors_AllTags(t *testing.T) {
cv := NewCustomValidator()
type allTags struct {
Required string `json:"required" validate:"required"`
Email string `json:"email" validate:"email"`
MinLen string `json:"min_len" validate:"min=5"`
MaxLen string `json:"max_len" validate:"max=3"`
OneOf string `json:"one_of" validate:"oneof=a b c"`
URL string `json:"url" validate:"url"`
}
input := allTags{
Required: "", // fails required
Email: "bad", // fails email
MinLen: "ab", // fails min=5
MaxLen: "abcde", // fails max=3
OneOf: "z", // fails oneof
URL: "nope", // fails url
}
err := cv.Validate(input)
require.Error(t, err)
resp := FormatValidationErrors(err)
require.NotNil(t, resp)
assert.Equal(t, "Validation failed", resp.Error)
expectedMessages := map[string]string{
"required": "This field is required",
"email": "Must be a valid email address",
"min_len": "Must be at least 5 characters",
"max_len": "Must be at most 3 characters",
"one_of": "Must be one of: a b c",
"url": "Must be a valid URL",
}
for field, expectedMsg := range expectedMessages {
fe, ok := resp.Fields[field]
assert.True(t, ok, "expected field %q in error response", field)
if ok {
assert.Equal(t, expectedMsg, fe.Message, "message mismatch for field %q", field)
}
}
}
func TestFormatValidationErrors_NonValidationError(t *testing.T) {
err := fmt.Errorf("some random error")
resp := FormatValidationErrors(err)
require.NotNil(t, resp)
assert.Equal(t, "some random error", resp.Error)
assert.Nil(t, resp.Fields)
}
func TestNewCustomValidator_UsesJSONTagNames(t *testing.T) {
cv := NewCustomValidator()
type request struct {
FirstName string `json:"first_name" validate:"required"`
}
err := cv.Validate(request{})
require.Error(t, err)
resp := FormatValidationErrors(err)
require.NotNil(t, resp)
_, ok := resp.Fields["first_name"]
assert.True(t, ok, "expected JSON tag name 'first_name' in error fields")
}
func TestCustomValidator_Validate_Success(t *testing.T) {
cv := NewCustomValidator()
type request struct {
Name string `json:"name" validate:"required"`
}
err := cv.Validate(request{Name: "test"})
assert.NoError(t, err)
}