Compare commits

...

2 Commits

Author SHA1 Message Date
Adam Veldhousen 4ab08d20b8
grpc reflection
ci.vdhsn.com/push Build is failing Details
2023-07-01 03:07:36 -05:00
Adam Veldhousen fe329d2336
tests for auth 2023-07-01 03:07:22 -05:00
5 changed files with 241 additions and 35 deletions

View File

@ -22,6 +22,11 @@ type (
} }
) )
var (
ErrAccountNotFound = errors.New("account not found")
ErrCouldNotCreateSession = errors.New("could not create session")
)
func (db *PGRunnerStorage) RegisterAccount(ctx context.Context, in RegisterAccountInput) (accountId int, err error) { func (db *PGRunnerStorage) RegisterAccount(ctx context.Context, in RegisterAccountInput) (accountId int, err error) {
var accId int32 var accId int32
if accId, err = db.Queries.RegisterAccount(ctx, postgres.RegisterAccountParams{ if accId, err = db.Queries.RegisterAccount(ctx, postgres.RegisterAccountParams{
@ -59,8 +64,11 @@ func (db *PGRunnerStorage) GetAccount(ctx context.Context, in GetAccountInput) (
if row, err = db.Queries.GetAccountByEmailOrID(ctx, postgres.GetAccountByEmailOrIDParams{ if row, err = db.Queries.GetAccountByEmailOrID(ctx, postgres.GetAccountByEmailOrIDParams{
Email: in.Email, Email: in.Email,
ID: int32(in.AccountID), ID: int32(in.AccountID),
}); err != nil { }); errors.Is(err, pgx.ErrNoRows) {
err = fmt.Errorf("could not get account: %w", err) err = ErrAccountNotFound
return
} else if err != nil {
err = fmt.Errorf("could not execute GetAccount query: %w", err)
return return
} }
@ -95,6 +103,7 @@ func (db *PGRunnerStorage) CreateSession(ctx context.Context, in CreateSessionIn
Email: in.Email, Email: in.Email,
Passwordhash: in.PasswordHash, Passwordhash: in.PasswordHash,
}); errors.Is(err, pgx.ErrNoRows) { }); errors.Is(err, pgx.ErrNoRows) {
err = ErrCouldNotCreateSession
return return
} else if err != nil { } else if err != nil {
err = fmt.Errorf("could not execute query: %w", err) err = fmt.Errorf("could not execute query: %w", err)

View File

@ -8,7 +8,6 @@ import (
"git.vdhsn.com/barretthousen/barretthousen/src/auth/internal/data" "git.vdhsn.com/barretthousen/barretthousen/src/auth/internal/data"
"git.vdhsn.com/barretthousen/barretthousen/src/lib/kernel" "git.vdhsn.com/barretthousen/barretthousen/src/lib/kernel"
"github.com/jackc/pgx/v4"
) )
type ( type (
@ -79,12 +78,13 @@ func (d *Domain) CreateAccount(ctx context.Context, in CreateAccountCommand) (ou
out = AccountCreated{ out = AccountCreated{
Account: Account{ Account: Account{
ID: ar.ID, ID: ar.ID,
Created: ar.Created, Created: ar.Created,
Verified: ar.Verified, Verified: ar.Verified,
Email: ar.Email, Email: ar.Email,
Role: Role(ar.Role), PasswordHash: ar.PasswordHash,
Enabled: ar.Enabled, Role: Role(ar.Role),
Enabled: ar.Enabled,
}, },
} }
@ -111,11 +111,12 @@ func (d *Domain) Login(ctx context.Context, in LoginCommand) (out LoggedIn, err
if sess, err = d.Storage.CreateSession(ctx, data.CreateSessionInput{ if sess, err = d.Storage.CreateSession(ctx, data.CreateSessionInput{
Email: in.Email, Email: in.Email,
PasswordHash: in.Password, PasswordHash: in.Password,
}); errors.Is(err, pgx.ErrNoRows) { }); errors.Is(err, data.ErrCouldNotCreateSession) {
err = ErrInvalidLogin err = ErrInvalidLogin
return return
} else if err != nil { } else if err != nil {
err = fmt.Errorf("could not create session: %w", err) kernel.ErrorLog.Printf("Error creating session in storage for %q: %w", in.Email, err)
err = ErrInvalidLogin
return return
} }

View File

@ -1,39 +1,166 @@
package domain package domain
import ( import (
"context"
"errors"
"reflect" "reflect"
"strings"
"testing" "testing"
"git.vdhsn.com/barretthousen/barretthousen/src/auth/internal/data"
) )
func TestDomain_CreateAccount(t *testing.T) { func TestDomain_CreateAccount(t *testing.T) {
type fields struct { SetupTest()
Storage Storage
} tests := map[string]struct {
type args struct { input CreateAccountCommand
in CreateAccountCommand
}
tests := []struct {
name string
fields fields
args args
wantOut AccountCreated wantOut AccountCreated
wantErr bool wantErr bool
}{ }{
// TODO: Add test cases. "new account": {
input: CreateAccountCommand{
Email: "test@example.com",
Password: "password",
},
wantOut: AccountCreated{
Account: Account{
ID: 0,
Email: "test@example.com",
PasswordHash: "password",
Enabled: true,
Role: UserRole,
},
},
},
"no email": {
input: CreateAccountCommand{
Password: "password",
Role: UserRole,
},
wantErr: true,
},
"no password": {
input: CreateAccountCommand{
Email: "test@example.com",
Role: UserRole,
},
wantErr: true,
},
"no inputs": {
wantErr: true,
},
} }
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { c := ioc.Scope("CreateAccountTest")
d := &Domain{
Storage: tt.fields.Storage, Must(c.Provide(func(s Storage, ms *MockStorage) *Domain {
} return &Domain{Storage: s}
gotOut, err := d.CreateAccount(tt.args.in) }))
if (err != nil) != tt.wantErr {
t.Errorf("Domain.CreateAccount() error = %v, wantErr %v", err, tt.wantErr) for name, v := range tests {
return params := v
} t.Run(name, func(t *testing.T) {
if !reflect.DeepEqual(gotOut, tt.wantOut) { Must(c.Invoke(func(sut *Domain, ms *MockStorage) {
t.Errorf("Domain.CreateAccount() = %v, want %v", gotOut, tt.wantOut) var a Account
} ms.RegisterAccountFunc = func(ctx context.Context, rai data.RegisterAccountInput) (int, error) {
a = Account{
Email: rai.Email,
PasswordHash: rai.PasswordHash,
Role: Role(rai.Role),
Enabled: true,
}
return 0, nil
}
ms.GetAccountFunc = func(ctx context.Context, gai data.GetAccountInput) (data.AccountResult, error) {
if gai.Email != a.Email {
return data.AccountResult{}, errors.New("error")
}
return data.AccountResult{
Email: a.Email,
PasswordHash: a.PasswordHash,
Role: a.Role.String(),
Enabled: true,
}, nil
}
out, err := sut.CreateAccount(context.Background(), params.input)
if (err != nil) != params.wantErr {
t.Errorf("Domain.CreateAccount() error = %v, wantErr %v", err, params.wantErr)
return
}
if !reflect.DeepEqual(out, params.wantOut) {
t.Errorf("Domain.CreateAccount():\n%+v\n, want:\n%+v", out, params.wantOut)
}
}))
})
}
}
func TestDomain_Login(t *testing.T) {
tests := map[string]struct {
input LoginCommand
wantOut LoggedIn
wantErr bool
}{
"Login Valid User": {
input: LoginCommand{
Email: "test@example.com",
Password: "password",
},
wantOut: LoggedIn{
Account: Account{
ID: 1,
Email: "test@example.com",
PasswordHash: "",
Enabled: true,
Role: UserRole,
},
},
},
}
c := ioc.Scope("LoginTest")
Must(c.Provide(func(s Storage) *Domain {
return &Domain{Storage: s}
}))
for name, v := range tests {
params := v
t.Run(name, func(t *testing.T) {
Must(c.Invoke(func(sut *Domain, ms *MockStorage) {
ms.CreateSessionFunc = func(ctx context.Context, in data.CreateSessionInput) (data.Session, error) {
if strings.EqualFold(in.Email, "test@example.com") {
return data.Session{AccountID: 1}, nil
}
return data.Session{}, data.ErrCouldNotCreateSession
}
ms.GetAccountFunc = func(ctx context.Context, gai data.GetAccountInput) (data.AccountResult, error) {
if strings.EqualFold(gai.Email, "test@example.com") || gai.AccountID == 1 {
return data.AccountResult{
ID: 1,
Email: "test@example.com",
Role: UserRole.String(),
Enabled: true,
}, nil
}
return data.AccountResult{}, data.ErrAccountNotFound
}
out, err := sut.Login(context.Background(), params.input)
if (err != nil) != params.wantErr {
t.Errorf("Domain.Login() error = %v, wantErr %v", err, params.wantErr)
return
}
if !reflect.DeepEqual(out, params.wantOut) {
t.Errorf("Domain.Login() = %v, want %v", out, params.wantOut)
}
}))
}) })
} }
} }

View File

@ -0,0 +1,65 @@
package domain
import (
"context"
"git.vdhsn.com/barretthousen/barretthousen/src/auth/internal/data"
"go.uber.org/dig"
)
var ioc dig.Container
func SetupTest() {
ioc = *dig.New()
if err := ioc.Provide(func() (Storage, *MockStorage) {
ms := &MockStorage{}
return ms, ms
}); err != nil {
panic(err)
}
}
// Must panics is err is not nil
func Must(err error) {
if err != nil {
panic(err)
}
}
type MockStorage struct {
RegisterAccountFunc func(context.Context, data.RegisterAccountInput) (int, error)
GetAccountFunc func(context.Context, data.GetAccountInput) (data.AccountResult, error)
CreateSessionFunc func(context.Context, data.CreateSessionInput) (data.Session, error)
}
// CreateSession implements Storage.
func (m *MockStorage) CreateSession(ctx context.Context, in data.CreateSessionInput) (data.Session, error) {
if m.CreateSessionFunc == nil {
panic("CreateSessionFunc is unset")
}
return m.CreateSessionFunc(ctx, in)
}
// GetAccount implements Storage.
func (m *MockStorage) GetAccount(ctx context.Context, in data.GetAccountInput) (data.AccountResult, error) {
if m.GetAccountFunc == nil {
panic("GetAccountFunc is unset")
}
return m.GetAccountFunc(ctx, in)
}
// RegisterAccount implements Storage.
func (m *MockStorage) RegisterAccount(ctx context.Context, in data.RegisterAccountInput) (int, error) {
if m.RegisterAccountFunc == nil {
panic("RegisterAccountFunc is unset")
}
return m.RegisterAccountFunc(ctx, in)
}
// VerifyAccount implements Storage.
func (*MockStorage) VerifyAccount(context.Context, data.VerifyAccountInput) error {
panic("unimplemented")
}

View File

@ -10,8 +10,10 @@ import (
"time" "time"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/backoff" "google.golang.org/grpc/backoff"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
) )
type ServerBuilder func(grpc.ServiceRegistrar, string) type ServerBuilder func(grpc.ServiceRegistrar, string)
@ -38,6 +40,8 @@ func StartGRPCServer(ctx context.Context, port int, sb ServerBuilder, opts ...gr
grpcServerInstance = grpc.NewServer(opts...) grpcServerInstance = grpc.NewServer(opts...)
reflection.Register(grpcServerInstance)
sb(grpcServerInstance, endpoint) sb(grpcServerInstance, endpoint)
if err = grpcServerInstance.Serve(listener); err != nil { if err = grpcServerInstance.Serve(listener); err != nil {