Skip to content

Commit f71899e

Browse files
committed
test: add unit tests for AuditAccounts function with failing store and serial mode delegation
1 parent d168a49 commit f71899e

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package core
2+
3+
import (
4+
"context"
5+
"errors"
6+
"strings"
7+
"testing"
8+
9+
"github.com/toeirei/keymaster/internal/model"
10+
"github.com/toeirei/keymaster/internal/security"
11+
)
12+
13+
// fake store that fails UpdateAccountIsDirty
14+
type failingDirtyStore struct {
15+
accounts []model.Account
16+
}
17+
18+
func (s *failingDirtyStore) GetAccounts() ([]model.Account, error) { return nil, nil }
19+
func (s *failingDirtyStore) GetAllActiveAccounts() ([]model.Account, error) { return s.accounts, nil }
20+
func (s *failingDirtyStore) GetAllAccounts() ([]model.Account, error) { return nil, nil }
21+
func (s *failingDirtyStore) GetAccount(id int) (*model.Account, error) { return nil, nil }
22+
func (s *failingDirtyStore) AddAccount(username, hostname, label, tags string) (int, error) {
23+
return 0, nil
24+
}
25+
func (s *failingDirtyStore) DeleteAccount(accountID int) error { return nil }
26+
func (s *failingDirtyStore) AssignKeyToAccount(keyID, accountID int) error { return nil }
27+
func (s *failingDirtyStore) UpdateAccountIsDirty(id int, dirty bool) error {
28+
return errors.New("update fail")
29+
}
30+
func (s *failingDirtyStore) CreateSystemKey(publicKey, privateKey string) (int, error) { return 0, nil }
31+
func (s *failingDirtyStore) RotateSystemKey(publicKey, privateKey string) (int, error) { return 0, nil }
32+
func (s *failingDirtyStore) GetActiveSystemKey() (*model.SystemKey, error) {
33+
return &model.SystemKey{Serial: 1, PublicKey: "sys"}, nil
34+
}
35+
func (s *failingDirtyStore) AddKnownHostKey(hostname, key string) error { return nil }
36+
func (s *failingDirtyStore) ExportDataForBackup() (*model.BackupData, error) { return nil, nil }
37+
func (s *failingDirtyStore) ImportDataFromBackup(*model.BackupData) error { return nil }
38+
func (s *failingDirtyStore) IntegrateDataFromBackup(*model.BackupData) error { return nil }
39+
40+
// fake DM that returns mismatched content
41+
type mismatchDM struct{}
42+
43+
func (m *mismatchDM) DeployForAccount(account model.Account, keepFile bool) error { return nil }
44+
func (m *mismatchDM) AuditSerial(account model.Account) error { return nil }
45+
func (m *mismatchDM) AuditStrict(account model.Account) error { return nil }
46+
func (m *mismatchDM) DecommissionAccount(account model.Account, systemPrivateKey security.Secret, options interface{}) (DecommissionResult, error) {
47+
return DecommissionResult{}, nil
48+
}
49+
func (m *mismatchDM) BulkDecommissionAccounts(accounts []model.Account, systemPrivateKey security.Secret, options interface{}) ([]DecommissionResult, error) {
50+
return nil, nil
51+
}
52+
func (m *mismatchDM) CanonicalizeHostPort(host string) string { return host }
53+
func (m *mismatchDM) ParseHostPort(host string) (string, string, error) { return host, "22", nil }
54+
func (m *mismatchDM) GetRemoteHostKey(host string) (string, error) { return "hostkey", nil }
55+
func (m *mismatchDM) FetchAuthorizedKeys(account model.Account) ([]byte, error) {
56+
return []byte("different content"), nil
57+
}
58+
func (m *mismatchDM) ImportRemoteKeys(account model.Account) ([]model.PublicKey, int, string, error) {
59+
return nil, 0, "", nil
60+
}
61+
func (m *mismatchDM) IsPassphraseRequired(err error) bool { return false }
62+
63+
// serialDM records whether AuditSerial was invoked
64+
type serialDM struct{ Called *bool }
65+
66+
func (s *serialDM) DeployForAccount(account model.Account, keepFile bool) error { return nil }
67+
func (s *serialDM) AuditSerial(account model.Account) error {
68+
if s.Called != nil {
69+
*s.Called = true
70+
}
71+
return nil
72+
}
73+
func (s *serialDM) AuditStrict(account model.Account) error { return nil }
74+
func (s *serialDM) DecommissionAccount(account model.Account, systemPrivateKey security.Secret, options interface{}) (DecommissionResult, error) {
75+
return DecommissionResult{}, nil
76+
}
77+
func (s *serialDM) BulkDecommissionAccounts(accounts []model.Account, systemPrivateKey security.Secret, options interface{}) ([]DecommissionResult, error) {
78+
return nil, nil
79+
}
80+
func (s *serialDM) CanonicalizeHostPort(host string) string { return host }
81+
func (s *serialDM) ParseHostPort(host string) (string, string, error) { return host, "22", nil }
82+
func (s *serialDM) GetRemoteHostKey(host string) (string, error) { return "hostkey", nil }
83+
func (s *serialDM) FetchAuthorizedKeys(account model.Account) ([]byte, error) { return nil, nil }
84+
func (s *serialDM) ImportRemoteKeys(account model.Account) ([]model.PublicKey, int, string, error) {
85+
return nil, 0, "", nil
86+
}
87+
func (s *serialDM) IsPassphraseRequired(err error) bool { return false }
88+
89+
func TestAuditAccounts_MarkDirtyFailure_LogsFailure(t *testing.T) {
90+
// store with one active account
91+
acct := model.Account{ID: 42, Username: "u", Hostname: "h", Serial: 1, IsActive: true}
92+
store := &failingDirtyStore{accounts: []model.Account{acct}}
93+
dm := &mismatchDM{}
94+
95+
aw := &spyAuditWriter{}
96+
SetDefaultAuditWriter(aw)
97+
defer SetDefaultAuditWriter(nil)
98+
99+
SetDefaultKeyReader(&fakeKR{})
100+
SetDefaultKeyLister(&fakeKL{})
101+
102+
res, err := AuditAccounts(context.TODO(), store, dm, "strict", nil)
103+
if err != nil {
104+
t.Fatalf("unexpected err: %v", err)
105+
}
106+
if len(res) != 1 {
107+
t.Fatalf("expected 1 result")
108+
}
109+
// ensure mark-dirty failed event logged
110+
found := false
111+
for _, a := range aw.actions {
112+
if strings.HasPrefix(a, "AUDIT_HASH_MARK_DIRTY_FAILED") {
113+
found = true
114+
break
115+
}
116+
}
117+
if !found {
118+
t.Fatalf("expected AUDIT_HASH_MARK_DIRTY_FAILED in audit actions: %v", aw.actions)
119+
}
120+
}
121+
122+
// Test that AuditAccounts in 'serial' mode delegates to DeployerManager.AuditSerial
123+
func TestAuditAccounts_SerialMode_Delegates(t *testing.T) {
124+
acct := model.Account{ID: 99, Username: "u", Hostname: "h", Serial: 3, IsActive: true}
125+
store := &simpleFakeStore{accounts: []model.Account{acct}}
126+
called := false
127+
dm2 := &serialDM{Called: &called}
128+
_, err := AuditAccounts(context.TODO(), store, dm2, "serial", nil)
129+
if err != nil {
130+
t.Fatalf("unexpected err: %v", err)
131+
}
132+
if !called {
133+
t.Fatalf("expected AuditSerial to be called")
134+
}
135+
}

0 commit comments

Comments
 (0)