feat: complete remaining Go backend modules

This commit is contained in:
zn-admin committed 2026-08-14 00:02:43 +08:00
1 parent cea2751dc5
commit aef5a97165
145 files changed
+18376 -199

No files matched your search

@@ -217,3 +217,54 @@ func (s *fakeStore) DeleteOrganization(context.Context, string) error { return s
func (s *fakeStore) CountOrganizationMembers(context.Context, string) (int, error) {
return s.memberCount, s.err
}
func TestUpdateAccountPrefersAtomicIntentOverStaleFullRowWrite(t *testing.T) {
now := time.Date(2026, 8, 13, 8, 0, 0, 0, time.UTC)
base := &fakeStore{
accounts: map[string]Account{"user-1": {
ID: "user-1", DisplayName: "Before", Role: RoleUser, OrganizationID: "org-1", Status: StatusActive,
PasswordHash: "stale-hash", PasswordSalt: "stale-salt", SessionVersion: 4,
}},
organizations: map[string]Organization{"org-1": {ID: "org-1", Status: StatusActive}},
}
store := &atomicAccountStore{fakeStore: base, result: Account{
ID: "user-1", DisplayName: "After", Role: RoleUser, OrganizationID: "org-1", Status: StatusActive,
PasswordHash: "concurrent-password-hash", PasswordSalt: "concurrent-password-salt", SessionVersion: 8,
}}
service := NewService(store, WithClock(func() time.Time { return now }))
name := "After"
got, err := service.UpdateAccount(context.Background(), Actor{ID: "super", Role: RoleSuperAdmin}, "user-1", UpdateAccountInput{DisplayName: &name})
if err != nil {
t.Fatal(err)
}
if store.fullRowWrites != 0 {
t.Fatalf("full-row writes=%d, want zero", store.fullRowWrites)
}
if store.id != "user-1" || store.update.DisplayName == nil || *store.update.DisplayName != "After" {
t.Fatalf("atomic update=(id=%q, update=%#v)", store.id, store.update)
}
if store.update.PasswordHash != nil || store.update.IncrementSessionVersion {
t.Fatalf("display-only update must not overwrite password or increment session: %#v", store.update)
}
if got.PasswordHash != "concurrent-password-hash" || got.SessionVersion != 8 {
t.Fatalf("result=%#v, want database-current security state", got)
}
}
type atomicAccountStore struct {
*fakeStore
id string
update AccountUpdate
result Account
fullRowWrites int
}
func (s *atomicAccountStore) UpdateAccount(ctx context.Context, account Account) (Account, error) {
s.fullRowWrites++
return s.fakeStore.UpdateAccount(ctx, account)
}
func (s *atomicAccountStore) ApplyAccountUpdate(_ context.Context, id string, update AccountUpdate) (Account, error) {
s.id, s.update = id, update
return s.result, nil
}