diff --git a/core/api/account.go b/core/api/account.go index 5ea4146..276250e 100644 --- a/core/api/account.go +++ b/core/api/account.go @@ -2,7 +2,7 @@ package api import ( "encoding/json" - "io/ioutil" + "io" "net/http" "strconv" "time" @@ -12,48 +12,7 @@ import ( "github.com/openaccounting/oa-server/core/model/types" ) -/** - * @api {get} /orgs/:orgId/accounts Get Accounts by Org id - * @apiVersion 1.4.0 - * @apiName GetOrgAccounts - * @apiGroup Account - * - * @apiHeader {String} Authorization HTTP Basic Auth - * @apiHeader {String} Accept-Version ^1.4.0 semver versioning - * - * @apiSuccess {String} id Id of the Account. - * @apiSuccess {String} orgId Id of the Org. - * @apiSuccess {Date} inserted Date Account was created - * @apiSuccess {Date} updated Date Account was updated - * @apiSuccess {String} name Name of the Account. - * @apiSuccess {String} parent Id of the parent Account. - * @apiSuccess {String} currency Three letter currency code. - * @apiSuccess {Number} precision How many digits the currency goes out to. - * @apiSuccess {Boolean} debitBalance True if Account has a debit balance. - * @apiSuccess {Number} balance Current Account balance in this Account's currency - * @apiSuccess {Number} nativeBalance Current Account balance in the Org's currency - * - * @apiSuccessExample Success-Response: - * HTTP/1.1 200 OK - * [ - * { - * "id": "22222222222222222222222222222222", - * "orgId": "11111111111111111111111111111111", - * "inserted": "2018-09-11T18:05:04.420Z", - * "updated": "2018-09-11T18:05:04.420Z", - * "name": "Cash", - * "parent": "11111111111111111111111111111111", - * "currency": "USD", - * "precision": 2, - * "debitBalance": true, - * "balance": 10000, - * "nativeBalance": 10000 - * } - * ] - * - * @apiUse NotAuthorizedError - * @apiUse InternalServerError - */ +// GetOrgAccounts /** func GetOrgAccounts(w rest.ResponseWriter, r *rest.Request) { user := r.Env["USER"].(*types.User) orgId := r.PathParam("orgId") @@ -208,7 +167,7 @@ func PostAccount(w rest.ResponseWriter, r *rest.Request) { user := r.Env["USER"].(*types.User) orgId := r.PathParam("orgId") - content, err := ioutil.ReadAll(r.Body) + content, err := io.ReadAll(r.Body) r.Body.Close() if err != nil { diff --git a/core/mocks/Datastore.go b/core/mocks/Datastore.go index ac1c132..e494d81 100644 --- a/core/mocks/Datastore.go +++ b/core/mocks/Datastore.go @@ -10,6 +10,31 @@ type Datastore struct { mock.Mock } +// DeleteBudget implements db.Datastore. +func (_m *Datastore) DeleteBudget(string) error { + panic("unimplemented") +} + +// GetBudget implements db.Datastore. +func (_m *Datastore) GetBudget(string) (*types.Budget, error) { + panic("unimplemented") +} + +// GetUserByEmailVerifyCode implements db.Datastore. +func (_m *Datastore) GetUserByEmailVerifyCode(string) (*types.User, error) { + panic("unimplemented") +} + +// InsertAndReplaceBudget implements db.Datastore. +func (_m *Datastore) InsertAndReplaceBudget(*types.Budget) error { + panic("unimplemented") +} + +// Ping implements db.Datastore. +func (_m *Datastore) Ping() error { + panic("unimplemented") +} + // AcceptInvite provides a mock function with given fields: _a0, _a1 func (_m *Datastore) AcceptInvite(_a0 *types.Invite, _a1 string) error { ret := _m.Called(_a0, _a1) diff --git a/core/model/budget.go b/core/model/budget.go index e1798fa..5715ace 100644 --- a/core/model/budget.go +++ b/core/model/budget.go @@ -2,6 +2,7 @@ package model import ( "errors" + "github.com/openaccounting/oa-server/core/model/types" ) @@ -18,8 +19,8 @@ func (model *Model) GetBudget(orgId string, userId string) (*types.Budget, error return nil, err } - if belongs == false { - return nil, errors.New("User does not belong to org") + if !belongs { + return nil, errors.New("user does not belong to org") } return model.db.GetBudget(orgId) @@ -32,8 +33,8 @@ func (model *Model) CreateBudget(budget *types.Budget, userId string) error { return err } - if belongs == false { - return errors.New("User does not belong to org") + if !belongs { + return errors.New("user does not belong to org") } if budget.OrgId == "" { @@ -50,8 +51,8 @@ func (model *Model) DeleteBudget(orgId string, userId string) error { return err } - if belongs == false { - return errors.New("User does not belong to org") + if !belongs { + return errors.New("user does not belong to org") } return model.db.DeleteBudget(orgId) diff --git a/core/model/gorm_model.go b/core/model/gorm_model.go new file mode 100644 index 0000000..7cc52cc --- /dev/null +++ b/core/model/gorm_model.go @@ -0,0 +1,321 @@ +package model + +import ( + "errors" + "time" + + "github.com/openaccounting/oa-server/core/model/types" + "github.com/openaccounting/oa-server/core/repository" + "github.com/openaccounting/oa-server/core/util" + "github.com/openaccounting/oa-server/database" + "gorm.io/gorm" +) + +// GormModel is the GORM-based implementation of the Model +type GormModel struct { + repository *repository.GormRepository + bcrypt util.Bcrypt + config types.Config +} + +// NewGormModel creates a new GORM-based model +func NewGormModel(gormDB *gorm.DB, bcrypt util.Bcrypt, config types.Config) *GormModel { + repo := repository.NewGormRepository(gormDB) + return &GormModel{ + repository: repo, + bcrypt: bcrypt, + config: config, + } +} + +// CreateModel creates a new model using the existing database connection +func CreateGormModel(bcrypt util.Bcrypt, config types.Config) (*GormModel, error) { + // Use the existing database connection + if database.DB == nil { + return nil, errors.New("database connection not initialized") + } + + return NewGormModel(database.DB, bcrypt, config), nil +} + +// Implement the Interface by delegating to the business logic layer +// The business logic layer (existing model methods) will call the repository + +// UserInterface methods - delegate to existing business logic +func (m *GormModel) CreateUser(user *types.User) error { + // The existing business logic in user.go will be updated to use the repository + // For now, delegate directly to repository for basic operations + return m.repository.InsertUser(user) +} + +func (m *GormModel) VerifyUser(code string) error { + return m.repository.VerifyUser(code) +} + +func (m *GormModel) UpdateUser(user *types.User) error { + return m.repository.UpdateUser(user) +} + +func (m *GormModel) ResetPassword(email string) error { + // This would need the full business logic from the original model + // For now, simplified implementation + user, err := m.repository.GetVerifiedUserByEmail(email) + if err != nil { + return err + } + + user.PasswordReset, err = util.NewGuid() + if err != nil { + return err + } + + return m.repository.UpdateUserResetPassword(user) +} + +func (m *GormModel) ConfirmResetPassword(password string, code string) (*types.User, error) { + user, err := m.repository.GetUserByResetCode(code) + if err != nil { + return nil, err + } + + passwordHash, err := m.bcrypt.GenerateFromPassword([]byte(password), m.bcrypt.GetDefaultCost()) + if err != nil { + return nil, err + } + + user.PasswordHash = string(passwordHash) + user.Password = "" + + err = m.repository.UpdateUser(user) + if err != nil { + return nil, err + } + + return user, nil +} + +// AccountInterface methods - delegate to repository +func (m *GormModel) CreateAccount(account *types.Account, userId string) error { + return m.repository.InsertAccount(account) +} + +func (m *GormModel) UpdateAccount(account *types.Account, userId string) error { + return m.repository.UpdateAccount(account) +} + +func (m *GormModel) DeleteAccount(id string, userId string, orgId string) error { + return m.repository.DeleteAccount(id) +} + +func (m *GormModel) GetAccounts(orgId string, userId string, tokenId string) ([]*types.Account, error) { + return m.repository.GetAccountsByOrgId(orgId) +} + +func (m *GormModel) GetAccountsWithBalances(orgId string, userId string, tokenId string, date time.Time) ([]*types.Account, error) { + accounts, err := m.repository.GetAccountsByOrgId(orgId) + if err != nil { + return nil, err + } + + // Add balance calculations + err = m.repository.AddBalances(accounts, date) + if err != nil { + return nil, err + } + + return accounts, nil +} + +func (m *GormModel) GetAccount(orgId, accId, userId, tokenId string) (*types.Account, error) { + return m.repository.GetAccount(accId) +} + +func (m *GormModel) GetAccountWithBalance(orgId, accId, userId, tokenId string, date time.Time) (*types.Account, error) { + account, err := m.repository.GetAccount(accId) + if err != nil { + return nil, err + } + + // Add balance calculation + err = m.repository.AddBalance(account, date) + if err != nil { + return nil, err + } + + return account, nil +} + +// Complete OrgInterface implementation +func (m *GormModel) CreateOrg(org *types.Org, userId string) error { + // Get default accounts - this needs to be implemented properly + accounts := []*types.Account{} // Empty for now, should create default chart of accounts + return m.repository.CreateOrg(org, userId, accounts) +} + +func (m *GormModel) GetOrg(orgId, userId string) (*types.Org, error) { + return m.repository.GetOrg(orgId, userId) +} + +func (m *GormModel) GetOrgs(userId string) ([]*types.Org, error) { + return m.repository.GetOrgs(userId) +} + +func (m *GormModel) UpdateOrg(org *types.Org, userId string) error { + return m.repository.UpdateOrg(org) +} + +func (m *GormModel) CreateInvite(invite *types.Invite, userId string) error { + return m.repository.InsertInvite(invite) +} + +func (m *GormModel) AcceptInvite(invite *types.Invite, userId string) error { + return m.repository.AcceptInvite(invite, userId) +} + +func (m *GormModel) GetInvites(orgId, userId string) ([]*types.Invite, error) { + return m.repository.GetInvites(orgId) +} + +func (m *GormModel) DeleteInvite(inviteId, userId string) error { + return m.repository.DeleteInvite(inviteId) +} + +// SessionInterface implementation +func (m *GormModel) CreateSession(session *types.Session) error { + return m.repository.InsertSession(session) +} + +func (m *GormModel) InsertSession(session *types.Session) error { + return m.repository.InsertSession(session) +} + +func (m *GormModel) DeleteSession(sessionId, userId string) error { + return m.repository.DeleteSession(sessionId, userId) +} + +func (m *GormModel) UpdateSessionActivity(sessionId string) error { + return m.repository.UpdateSessionActivity(sessionId) +} + +// ApiKeyInterface implementation +func (m *GormModel) CreateApiKey(apiKey *types.ApiKey) error { + return m.repository.InsertApiKey(apiKey) +} + +func (m *GormModel) InsertApiKey(apiKey *types.ApiKey) error { + return m.repository.InsertApiKey(apiKey) +} + +func (m *GormModel) UpdateApiKey(apiKey *types.ApiKey) error { + return m.repository.UpdateApiKey(apiKey) +} + +func (m *GormModel) DeleteApiKey(keyId, userId string) error { + return m.repository.DeleteApiKey(keyId, userId) +} + +func (m *GormModel) GetApiKeys(userId string) ([]*types.ApiKey, error) { + return m.repository.GetApiKeys(userId) +} + +func (m *GormModel) UpdateApiKeyActivity(keyId string) error { + return m.repository.UpdateApiKeyActivity(keyId) +} + +// TransactionInterface implementation +func (m *GormModel) CreateTransaction(transaction *types.Transaction) error { + return m.repository.InsertTransaction(transaction) +} + +func (m *GormModel) UpdateTransaction(transactionId string, transaction *types.Transaction) error { + return m.repository.DeleteAndInsertTransaction(transactionId, transaction) +} + +func (m *GormModel) GetTransactionsByAccount(accountId, orgId, userId string, options *types.QueryOptions) ([]*types.Transaction, error) { + return m.repository.GetTransactionsByAccount(accountId, options) +} + +func (m *GormModel) GetTransactionsByOrg(orgId, userId string, options *types.QueryOptions) ([]*types.Transaction, error) { + return m.repository.GetTransactionsByOrg(orgId, options, []string{}) +} + +func (m *GormModel) DeleteTransaction(transactionId, orgId, userId string) error { + return m.repository.DeleteTransaction(transactionId) +} + +func (m *GormModel) InsertTransaction(transaction *types.Transaction) error { + return m.repository.InsertTransaction(transaction) +} + +func (m *GormModel) GetTransactionById(id string) (*types.Transaction, error) { + return m.repository.GetTransactionById(id) +} + +func (m *GormModel) DeleteAndInsertTransaction(id string, transaction *types.Transaction) error { + return m.repository.DeleteAndInsertTransaction(id, transaction) +} + +// PriceInterface implementation +func (m *GormModel) CreatePrice(price *types.Price, userId string) error { + return m.repository.InsertPrice(price) +} + +func (m *GormModel) DeletePrice(priceId, userId string) error { + // Stub implementation - would need proper implementation + return nil +} + +func (m *GormModel) GetPricesNearestInTime(orgId string, date time.Time, currency string) ([]*types.Price, error) { + // Stub implementation - would need proper implementation based on specific logic + return m.repository.GetPrices(orgId, date) +} + +func (m *GormModel) GetPricesByCurrency(orgId, currency, userId string) ([]*types.Price, error) { + // Stub implementation - would need proper implementation based on specific logic + return m.repository.GetPrices(orgId, time.Now()) +} + +func (m *GormModel) GetPrices(orgId string, date time.Time) ([]*types.Price, error) { + return m.repository.GetPrices(orgId, date) +} + +func (m *GormModel) InsertPrice(price *types.Price) error { + return m.repository.InsertPrice(price) +} + +// SystemHealthInteface implementation +func (m *GormModel) PingDatabase() error { + return m.repository.Ping() +} + +func (m *GormModel) Ping() error { + return m.repository.Ping() +} + +// BudgetInterface implementation +func (m *GormModel) GetBudget(orgId, userId string) (*types.Budget, error) { + // Stub implementation - would need proper implementation + return &types.Budget{}, nil +} + +func (m *GormModel) CreateBudget(budget *types.Budget, userId string) error { + return m.repository.InsertBudget(budget) +} + +func (m *GormModel) DeleteBudget(budgetId, userId string) error { + // Stub implementation - would need proper implementation + return nil +} + +func (m *GormModel) InsertBudget(budget *types.Budget) error { + return m.repository.InsertBudget(budget) +} + +func (m *GormModel) GetBudgets(orgId string) ([]*types.Budget, error) { + return m.repository.GetBudgets(orgId) +} + +// Helper methods +func (m *GormModel) GetOrgUserIds(orgId string) ([]string, error) { + return m.repository.GetOrgUserIds(orgId) +} \ No newline at end of file diff --git a/core/model/model.go b/core/model/model.go index a053910..48e4e8a 100644 --- a/core/model/model.go +++ b/core/model/model.go @@ -14,6 +14,7 @@ type Model struct { config types.Config } + type Interface interface { UserInterface OrgInterface @@ -31,3 +32,4 @@ func NewModel(db db.Datastore, bcrypt util.Bcrypt, config types.Config) *Model { Instance = model return model } + diff --git a/core/model/price_test.go b/core/model/price_test.go index e3f09cc..3f919d1 100644 --- a/core/model/price_test.go +++ b/core/model/price_test.go @@ -2,44 +2,45 @@ package model import ( "errors" + "testing" + "time" + "github.com/openaccounting/oa-server/core/mocks" "github.com/openaccounting/oa-server/core/model/types" "github.com/openaccounting/oa-server/core/util" "github.com/stretchr/testify/assert" - "testing" - "time" ) func TestCreatePrice(t *testing.T) { price := types.Price{ - "1", - "2", - "BTC", - time.Unix(0, 0), - time.Unix(0, 0), - time.Unix(0, 0), - 6700, + Id: "1", + OrgId: "2", + Currency: "BTC", + Date: time.Unix(0, 0), + Inserted: time.Unix(0, 0), + Updated: time.Unix(0, 0), + Price: 6700, } badPrice := types.Price{ - "1", - "2", - "", - time.Unix(0, 0), - time.Unix(0, 0), - time.Unix(0, 0), - 6700, + Id: "1", + OrgId: "2", + Currency: "", + Date: time.Unix(0, 0), + Inserted: time.Unix(0, 0), + Updated: time.Unix(0, 0), + Price: 6700, } badOrg := types.Price{ - "1", - "1", - "BTC", - time.Unix(0, 0), - time.Unix(0, 0), - time.Unix(0, 0), - 6700, + Id: "1", + OrgId: "1", + Currency: "BTC", + Date: time.Unix(0, 0), + Inserted: time.Unix(0, 0), + Updated: time.Unix(0, 0), + Price: 6700, } tests := map[string]struct { @@ -89,13 +90,13 @@ func TestCreatePrice(t *testing.T) { func TestDeletePrice(t *testing.T) { price := types.Price{ - "1", - "2", - "BTC", - time.Unix(0, 0), - time.Unix(0, 0), - time.Unix(0, 0), - 6700, + Id: "1", + OrgId: "2", + Currency: "BTC", + Date: time.Unix(0, 0), + Inserted: time.Unix(0, 0), + Updated: time.Unix(0, 0), + Price: 6700, } tests := map[string]struct { diff --git a/core/model/transaction.go b/core/model/transaction.go index 103ad26..87efb60 100644 --- a/core/model/transaction.go +++ b/core/model/transaction.go @@ -3,9 +3,10 @@ package model import ( "errors" "fmt" + "time" + "github.com/openaccounting/oa-server/core/model/types" "github.com/openaccounting/oa-server/core/ws" - "time" ) type TransactionInterface interface { @@ -105,7 +106,7 @@ func (model *Model) GetTransactionsByAccount(orgId string, userId string, accoun } if !model.accountsContainWriteAccess(userAccounts, accountId) { - return nil, errors.New(fmt.Sprintf("%s %s", "user does not have permission to access account", accountId)) + return nil, fmt.Errorf("%s %s", "user does not have permission to access account", accountId) } return model.db.GetTransactionsByAccount(accountId, options) @@ -142,7 +143,7 @@ func (model *Model) DeleteTransaction(id string, userId string, orgId string) (e for _, split := range transaction.Splits { if !model.accountsContainWriteAccess(userAccounts, split.AccountId) { - return errors.New(fmt.Sprintf("%s %s", "user does not have permission to access account", split.AccountId)) + return fmt.Errorf("%s %s", "user does not have permission to access account", split.AccountId) } } @@ -189,13 +190,13 @@ func (model *Model) checkSplits(transaction *types.Transaction) (err error) { for _, split := range transaction.Splits { if !model.accountsContainWriteAccess(userAccounts, split.AccountId) { - return errors.New(fmt.Sprintf("%s %s", "user does not have permission to access account", split.AccountId)) + return fmt.Errorf("%s %s", "user does not have permission to access account", split.AccountId) } account := model.getAccountFromList(userAccounts, split.AccountId) - if account.HasChildren == true { - return errors.New("Cannot use parent account for split") + if !account.HasChildren { + return errors.New("cannot use parent account for split") } if account.Currency == org.Currency && split.NativeAmount != split.Amount { diff --git a/core/model/transaction_test.go b/core/model/transaction_test.go index 7546f4c..1b6877b 100644 --- a/core/model/transaction_test.go +++ b/core/model/transaction_test.go @@ -2,12 +2,13 @@ package model import ( "errors" + "testing" + "time" + "github.com/openaccounting/oa-server/core/model/db" "github.com/openaccounting/oa-server/core/model/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "testing" - "time" ) type TdTransaction struct { @@ -57,72 +58,72 @@ func TestCreateTransaction(t *testing.T) { "successful": { err: nil, tx: &types.Transaction{ - "1", - "2", - "3", - time.Now(), - time.Now(), - time.Now(), - "description", - "", - false, - []*types.Split{ - &types.Split{"1", "1", 1000, 1000}, - &types.Split{"1", "2", -1000, -1000}, + Id: "1", + OrgId: "2", + UserId: "3", + Date: time.Now(), + Inserted: time.Now(), + Updated: time.Now(), + Description: "description", + Data: "", + Deleted: false, + Splits: []*types.Split{ + &types.Split{TransactionId: "1", AccountId: "1", Amount: 1000, NativeAmount: 1000}, + &types.Split{TransactionId: "1", AccountId: "2", Amount: -1000, NativeAmount: -1000}, }, }, }, "bad split amounts": { err: errors.New("splits must add up to 0"), tx: &types.Transaction{ - "1", - "2", - "3", - time.Now(), - time.Now(), - time.Now(), - "description", - "", - false, - []*types.Split{ - &types.Split{"1", "1", 1000, 1000}, - &types.Split{"1", "2", -500, -500}, + Id: "1", + OrgId: "2", + UserId: "3", + Date: time.Now(), + Inserted: time.Now(), + Updated: time.Now(), + Description: "description", + Data: "", + Deleted: false, + Splits: []*types.Split{ + &types.Split{TransactionId: "1", AccountId: "1", Amount: 1000, NativeAmount: 1000}, + &types.Split{TransactionId: "1", AccountId: "2", Amount: -500, NativeAmount: -500}, }, }, }, "lacking permission": { err: errors.New("user does not have permission to access account 3"), tx: &types.Transaction{ - "1", - "2", - "3", - time.Now(), - time.Now(), - time.Now(), - "description", - "", - false, - []*types.Split{ - &types.Split{"1", "1", 1000, 1000}, - &types.Split{"1", "3", -1000, -1000}, + Id: "1", + OrgId: "2", + UserId: "3", + Date: time.Now(), + Inserted: time.Now(), + Updated: time.Now(), + Description: "description", + Data: "", + Deleted: false, + Splits: []*types.Split{ + &types.Split{TransactionId: "1", AccountId: "1", Amount: 1000, NativeAmount: 1000}, + &types.Split{TransactionId: "1", AccountId: "3", Amount: -1000, NativeAmount: -1000}, }, }, }, "nativeAmount mismatch": { err: errors.New("nativeAmount must equal amount for native currency splits"), tx: &types.Transaction{ - "1", - "2", - "3", - time.Now(), - time.Now(), - time.Now(), - "description", - "", - false, - []*types.Split{ - &types.Split{"1", "1", 1000, 500}, - &types.Split{"1", "2", -1000, -500}, + Id: "1", + OrgId: "2", + UserId: "3", + Date: time.Now(), + Inserted: time.Now(), + Updated: time.Now(), + Description: "description", + Data: "", + Deleted: false, + Splits: []*types.Split{ + &types.Split{TransactionId: "1", AccountId: "1", Amount: 1000, NativeAmount: 500}, + &types.Split{TransactionId: "1", AccountId: "2", Amount: -1000, NativeAmount: -500}, }, }, }, diff --git a/core/model/types/config.go b/core/model/types/config.go index b2b821e..f6b98fb 100644 --- a/core/model/types/config.go +++ b/core/model/types/config.go @@ -1,18 +1,22 @@ package types type Config struct { - WebUrl string - Address string - Port int - ApiPrefix string - KeyFile string - CertFile string - DatabaseAddress string - Database string - User string - Password string - MailgunDomain string - MailgunKey string - MailgunEmail string - MailgunSender string + WebUrl string `mapstructure:"weburl"` + Address string `mapstructure:"address"` + Port int `mapstructure:"port"` + ApiPrefix string `mapstructure:"apiprefix"` + KeyFile string `mapstructure:"keyfile"` + CertFile string `mapstructure:"certfile"` + // Database configuration + DatabaseDriver string `mapstructure:"databasedriver"` // "mysql" or "sqlite" + DatabaseAddress string `mapstructure:"databaseaddress"` + Database string `mapstructure:"database"` + User string `mapstructure:"user"` + Password string `mapstructure:"password"` // Sensitive: use OA_PASSWORD env var + // SQLite specific + DatabaseFile string `mapstructure:"databasefile"` + MailgunDomain string `mapstructure:"mailgundomain"` + MailgunKey string `mapstructure:"mailgunkey"` // Sensitive: use OA_MAILGUN_KEY env var + MailgunEmail string `mapstructure:"mailgunemail"` + MailgunSender string `mapstructure:"mailgunsender"` } diff --git a/core/model/user.go b/core/model/user.go index d3be758..b30d586 100644 --- a/core/model/user.go +++ b/core/model/user.go @@ -37,7 +37,7 @@ func (model *Model) CreateUser(user *types.User) error { return errors.New("email required") } - re := regexp.MustCompile(".+@.+\\..+") + re := regexp.MustCompile(`.+@.+\..+`) if re.FindString(user.Email) == "" { return errors.New("invalid email address") @@ -47,7 +47,7 @@ func (model *Model) CreateUser(user *types.User) error { return errors.New("password required") } - if user.AgreeToTerms != true { + if !user.AgreeToTerms { return errors.New("must agree to terms") } @@ -123,7 +123,7 @@ func (model *Model) ResetPassword(email string) error { if err != nil { // Don't send back error so people can't try to find user accounts - log.Printf("Invalid email for reset password " + email) + log.Printf("Invalid email for reset password %s", email) return nil } @@ -154,7 +154,7 @@ func (model *Model) ConfirmResetPassword(password string, code string) (*types.U user, err := model.db.GetUserByResetCode(code) if err != nil { - return nil, errors.New("Invalid code") + return nil, errors.New("invalid code") } passwordHash, err := model.bcrypt.GenerateFromPassword([]byte(password), model.bcrypt.GetDefaultCost()) diff --git a/core/model/user_test.go b/core/model/user_test.go index 6d82e26..e3e3f96 100644 --- a/core/model/user_test.go +++ b/core/model/user_test.go @@ -2,12 +2,13 @@ package model import ( "errors" + "testing" + "time" + "github.com/openaccounting/oa-server/core/mocks" "github.com/openaccounting/oa-server/core/model/db" "github.com/openaccounting/oa-server/core/model/types" "github.com/stretchr/testify/assert" - "testing" - "time" ) type TdUser struct { @@ -39,33 +40,35 @@ func TestCreateUser(t *testing.T) { // EmailVerifyCode string `json:"-"` user := types.User{ - "0", - time.Unix(0, 0), - time.Unix(0, 0), - "John", - "Doe", - "johndoe@email.com", - "password", - "", - true, - "", - false, - "", + Id: "0", + Inserted: time.Unix(0, 0), + Updated: time.Unix(0, 0), + FirstName: "John", + LastName: "Doe", + Email: "johndoe@email.com", + Password: "password", + PasswordHash: "", + AgreeToTerms: true, + PasswordReset: "", + EmailVerified: false, + EmailVerifyCode: "", + SignupSource: "", } badUser := types.User{ - "0", - time.Unix(0, 0), - time.Unix(0, 0), - "John", - "Doe", - "", - "password", - "", - true, - "", - false, - "", + Id: "0", + Inserted: time.Unix(0, 0), + Updated: time.Unix(0, 0), + FirstName: "John", + LastName: "Doe", + Email: "", + Password: "password", + PasswordHash: "", + AgreeToTerms: true, + PasswordReset: "", + EmailVerified: false, + EmailVerifyCode: "", + SignupSource: "", } tests := map[string]struct { @@ -109,33 +112,35 @@ func TestCreateUser(t *testing.T) { func TestUpdateUser(t *testing.T) { user := types.User{ - "0", - time.Unix(0, 0), - time.Unix(0, 0), - "John2", - "Doe", - "johndoe@email.com", - "password", - "", - true, - "", - false, - "", + Id: "0", + Inserted: time.Unix(0, 0), + Updated: time.Unix(0, 0), + FirstName: "John2", + LastName: "Doe", + Email: "johndoe@email.com", + Password: "password", + PasswordHash: "", + AgreeToTerms: true, + PasswordReset: "", + EmailVerified: false, + EmailVerifyCode: "", + SignupSource: "", } badUser := types.User{ - "0", - time.Unix(0, 0), - time.Unix(0, 0), - "John2", - "Doe", - "johndoe@email.com", - "", - "", - true, - "", - false, - "", + Id: "0", + Inserted: time.Unix(0, 0), + Updated: time.Unix(0, 0), + FirstName: "John2", + LastName: "Doe", + Email: "johndoe@email.com", + Password: "", + PasswordHash: "", + AgreeToTerms: true, + PasswordReset: "", + EmailVerified: false, + EmailVerifyCode: "", + SignupSource: "", } tests := map[string]struct {