5 "github.com/jhalter/mobius/hotline"
6 "github.com/stretchr/testify/mock"
15 // loadFromYAMLFile loads data from a YAML file into the provided data structure.
16 func loadFromYAMLFile(path string, data interface{}) error {
17 fh, err := os.Open(path)
23 decoder := yaml.NewDecoder(fh)
24 return decoder.Decode(data)
27 type YAMLAccountManager struct {
28 accounts map[string]hotline.Account
34 func NewYAMLAccountManager(accountDir string) (*YAMLAccountManager, error) {
35 accountMgr := YAMLAccountManager{
36 accountDir: accountDir,
37 accounts: make(map[string]hotline.Account),
40 matches, err := filepath.Glob(filepath.Join(accountDir, "*.yaml"))
42 return nil, fmt.Errorf("list account files: %w", err)
45 if len(matches) == 0 {
46 return nil, fmt.Errorf("no accounts found in directory: %s", accountDir)
49 for _, filePath := range matches {
50 var account hotline.Account
51 fileContents, err := os.ReadFile(filePath)
53 return nil, fmt.Errorf("read file: %v", err)
56 if err := yaml.Unmarshal(fileContents, &account); err != nil {
57 return nil, fmt.Errorf("unmarshal: %v", err)
60 // Check the account file contents for a field name that only appears in the new AccessBitmap flag format.
61 // If not present, re-save the file to migrate it from the old array of ints format to new bool flag format.
62 if !strings.Contains(string(fileContents), " DownloadFile:") {
63 if err := accountMgr.Update(account, account.Login); err != nil {
64 return nil, fmt.Errorf("migrate account to new access flag format: %v", err)
68 accountMgr.accounts[account.Login] = account
71 return &accountMgr, nil
74 func (am *YAMLAccountManager) Create(account hotline.Account) error {
78 // Create account file, returning an error if one already exists.
79 file, err := os.OpenFile(
80 filepath.Join(am.accountDir, path.Join("/", account.Login+".yaml")),
81 os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644,
84 return fmt.Errorf("create account file: %w", err)
88 b, err := yaml.Marshal(account)
90 return fmt.Errorf("marshal account to YAML: %v", err)
93 _, err = file.Write(b)
95 return fmt.Errorf("write account file: %w", err)
98 am.accounts[account.Login] = account
103 func (am *YAMLAccountManager) Update(account hotline.Account, newLogin string) error {
107 // If the login has changed, rename the account file.
108 if account.Login != newLogin {
110 filepath.Join(am.accountDir, path.Join("/", account.Login)+".yaml"),
111 filepath.Join(am.accountDir, path.Join("/", newLogin)+".yaml"),
114 return fmt.Errorf("error renaming account file: %w", err)
117 account.Login = newLogin
118 am.accounts[newLogin] = account
120 delete(am.accounts, account.Login)
123 out, err := yaml.Marshal(&account)
128 if err := os.WriteFile(filepath.Join(am.accountDir, newLogin+".yaml"), out, 0644); err != nil {
129 return fmt.Errorf("error writing account file: %w", err)
132 am.accounts[account.Login] = account
137 func (am *YAMLAccountManager) Get(login string) *hotline.Account {
141 account, ok := am.accounts[login]
149 func (am *YAMLAccountManager) List() []hotline.Account {
153 var accounts []hotline.Account
154 for _, account := range am.accounts {
155 accounts = append(accounts, account)
161 func (am *YAMLAccountManager) Delete(login string) error {
165 err := os.Remove(filepath.Join(am.accountDir, path.Join("/", login+".yaml")))
167 return fmt.Errorf("delete account file: %v", err)
170 delete(am.accounts, login)
175 type MockAccountManager struct {
179 func (m *MockAccountManager) Create(account hotline.Account) error {
180 args := m.Called(account)
185 func (m *MockAccountManager) Update(account hotline.Account, newLogin string) error {
186 args := m.Called(account, newLogin)
191 func (m *MockAccountManager) Get(login string) *hotline.Account {
192 args := m.Called(login)
194 return args.Get(0).(*hotline.Account)
197 func (m *MockAccountManager) List() []hotline.Account {
200 return args.Get(0).([]hotline.Account)
203 func (m *MockAccountManager) Delete(login string) error {
204 args := m.Called(login)