feat: complete remaining Go backend modules
This commit is contained in:
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
|
||||
}
|
||||
Reference in new issue
Block a user