refactor(api): enforce protocol-neutral service boundaries
- refactor: move HTTP status and DTO concerns out of model and service layers into API and handler code. - feat: add admin service boundary and route auth through services instead of direct repository access. - test: add architecture and error tests covering package boundaries, redaction, and service behavior. - docs: record the protocol-neutral boundary decision and update architecture and roadmap notes.
This commit is contained in:
@@ -5,12 +5,13 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUserJSONOmitsStatusField(t *testing.T) {
|
||||
func TestUserJSONOmitsPasswordHash(t *testing.T) {
|
||||
u := User{
|
||||
ID: "user-1",
|
||||
Username: "alice",
|
||||
Email: "alice@example.com",
|
||||
Status: StatusActive,
|
||||
ID: "user-1",
|
||||
Username: "alice",
|
||||
Email: "alice@example.com",
|
||||
PasswordHash: "hash-secret",
|
||||
Status: StatusActive,
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(u)
|
||||
@@ -23,7 +24,57 @@ func TestUserJSONOmitsStatusField(t *testing.T) {
|
||||
t.Fatalf("json.Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if _, ok := m["status"]; ok {
|
||||
t.Error("Status field should not appear in JSON output")
|
||||
assertJSONKeysAbsent(t, m, "PasswordHash", "password_hash")
|
||||
}
|
||||
|
||||
func TestSessionJSONOmitsTokenHash(t *testing.T) {
|
||||
s := Session{
|
||||
ID: "session-1",
|
||||
UserID: "user-1",
|
||||
TokenHash: "token-hash-secret",
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Marshal: %v", err)
|
||||
}
|
||||
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
t.Fatalf("json.Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
assertJSONKeysAbsent(t, m, "TokenHash", "token_hash")
|
||||
}
|
||||
|
||||
func TestCredentialJSONOmitsSecretHash(t *testing.T) {
|
||||
c := Credential{
|
||||
ID: "credential-1",
|
||||
UserID: "user-1",
|
||||
Type: "app_passkey",
|
||||
Label: "Phone",
|
||||
SecretHash: "credential-secret",
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Marshal: %v", err)
|
||||
}
|
||||
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
t.Fatalf("json.Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
assertJSONKeysAbsent(t, m, "SecretHash", "secret_hash")
|
||||
}
|
||||
|
||||
func assertJSONKeysAbsent(t *testing.T, m map[string]interface{}, keys ...string) {
|
||||
t.Helper()
|
||||
|
||||
for _, key := range keys {
|
||||
if _, ok := m[key]; ok {
|
||||
t.Errorf("%s field should not appear in JSON output", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user