Implements /transaction/{id} GET method

Issue #14
This commit is contained in:
Luís Murta 2024-05-18 22:49:32 +01:00
parent a52bca5882
commit 8839dea7f5
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
6 changed files with 100 additions and 16 deletions

View File

@ -41,6 +41,18 @@ func (pf *ServerImpl) GetTransactions(ctx echo.Context, params GetTransactionsPa
return ctx.JSON(http.StatusOK, convertTransactions(transactions)) return ctx.JSON(http.StatusOK, convertTransactions(transactions))
} }
func (*ServerImpl) GetTransactionById(ctx echo.Context, transactionId int64) error { func (pf *ServerImpl) GetTransactionById(ctx echo.Context, transactionId int64) error {
return echo.NewHTTPError(http.StatusNotImplemented) log.Printf("GetTransactionById(%d)", transactionId)
transaction, err := pf.Dal.Transaction(transactionId)
if err != nil {
log.Printf("%v", err)
return ctx.NoContent(http.StatusInternalServerError)
}
if transaction == nil {
return ctx.NoContent(http.StatusNotFound)
}
return ctx.JSON(http.StatusOK, convertTransaction(*transaction))
} }

View File

@ -127,11 +127,14 @@ func TestServerImpl_GetTransactions(t *testing.T) {
} }
func TestServerImpl_GetTransactionById(t *testing.T) { func TestServerImpl_GetTransactionById(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mock_dal := mock.NewMockDAL(ctrl)
type fields struct { type fields struct {
Dal dal.DAL Dal dal.DAL
} }
type args struct { type args struct {
ctx echo.Context
transactionId int64 transactionId int64
} }
tests := []struct { tests := []struct {
@ -139,15 +142,36 @@ func TestServerImpl_GetTransactionById(t *testing.T) {
fields fields fields fields
args args args args
wantErr bool wantErr bool
mocks *entity.Transaction
}{ }{
// TODO: Add test cases. {
"200",
fields{mock_dal},
args{1},
false,
&entity.Transaction{Id: 1, Date: time.Now(), Description: "desc#1", Value: decimal.New(0, 0)},
},
{
"404",
fields{mock_dal},
args{2},
false,
nil,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
s := &ServerImpl{ s := &ServerImpl{
Dal: tt.fields.Dal, Dal: tt.fields.Dal,
} }
if err := s.GetTransactionById(tt.args.ctx, tt.args.transactionId); (err != nil) != tt.wantErr {
req := httptest.NewRequest(http.MethodGet, "/transaction", nil)
rec := httptest.NewRecorder()
ctx := echo.New().NewContext(req, rec)
mock_dal.EXPECT().Transaction(tt.args.transactionId).Return(tt.mocks, nil).Times(1)
if err := s.GetTransactionById(ctx, tt.args.transactionId); (err != nil) != tt.wantErr {
t.Errorf("ServerImpl.GetTransactionById() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("ServerImpl.GetTransactionById() error = %v, wantErr %v", err, tt.wantErr)
} }
}) })

View File

@ -3,7 +3,7 @@ package dal
import "git.rosemyrtle.work/personal-finance/server/internal/entity" import "git.rosemyrtle.work/personal-finance/server/internal/entity"
type DAL interface { type DAL interface {
Transaction() (entity.Transaction, error) Transaction(transactionId int64) (*entity.Transaction, error)
Transactions() (entity.Transactions, error) Transactions() (entity.Transactions, error)
Bank() (entity.Bank, error) Bank() (entity.Bank, error)
Banks() (entity.Banks, error) Banks() (entity.Banks, error)

View File

@ -12,8 +12,24 @@ type DalImpl struct {
Db *sql.DB Db *sql.DB
} }
func (*DalImpl) Transaction() (entity.Transaction, error) { func (dal *DalImpl) Transaction(transactionId int64) (*entity.Transaction, error) {
return entity.Transaction{}, errors.New("not implemented") log.Printf("DAL::Transaction(%d)", transactionId)
if dal.Db == nil {
log.Panic("database not available")
}
rows, err := dal.Db.Query("SELECT t.id, t.date, t.description, t.amount FROM pfbudget.transactions t WHERE t.id = $1", transactionId)
if err != nil {
return nil, err
}
transactions := convert[entity.Transaction](rows)
if len(transactions) == 0 {
return nil, nil
}
return &transactions[0], nil
} }
func (dal *DalImpl) Transactions() (entity.Transactions, error) { func (dal *DalImpl) Transactions() (entity.Transactions, error) {

View File

@ -13,23 +13,55 @@ import (
) )
func TestDalImpl_Transaction(t *testing.T) { func TestDalImpl_Transaction(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
date := time.Now()
type fields struct { type fields struct {
Db *sql.DB Db *sql.DB
} }
type args struct {
transactionId int64
rows [][]driver.Value
}
tests := []struct { tests := []struct {
name string name string
fields fields fields fields
want entity.Transaction args args
want *entity.Transaction
wantErr bool wantErr bool
}{ }{
// TODO: Add test cases. {"notfound", fields{db}, args{2, nil}, nil, false},
{
"found",
fields{db},
args{
1,
[][]driver.Value{
{1, date, "income", 1000},
}},
&entity.Transaction{Id: 1, Date: date, Description: "income", Value: decimal.NewFromInt(1000)},
false,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
d := &DalImpl{ d := &DalImpl{
Db: tt.fields.Db, Db: tt.fields.Db,
} }
got, err := d.Transaction()
mock.
ExpectQuery("^SELECT .* FROM .*transactions t WHERE t.id = \\$1$").
WithArgs(tt.args.transactionId).
WillReturnRows(
mock.
NewRows([]string{"id", "date", "description", "amount"}).
AddRows(tt.args.rows...),
)
got, err := d.Transaction(tt.args.transactionId)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("DalImpl.Transaction() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("DalImpl.Transaction() error = %v, wantErr %v", err, tt.wantErr)
return return

View File

@ -70,18 +70,18 @@ func (mr *MockDALMockRecorder) Banks() *gomock.Call {
} }
// Transaction mocks base method. // Transaction mocks base method.
func (m *MockDAL) Transaction() (entity.Transaction, error) { func (m *MockDAL) Transaction(arg0 int64) (*entity.Transaction, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Transaction") ret := m.ctrl.Call(m, "Transaction", arg0)
ret0, _ := ret[0].(entity.Transaction) ret0, _ := ret[0].(*entity.Transaction)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// Transaction indicates an expected call of Transaction. // Transaction indicates an expected call of Transaction.
func (mr *MockDALMockRecorder) Transaction() *gomock.Call { func (mr *MockDALMockRecorder) Transaction(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockDAL)(nil).Transaction)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockDAL)(nil).Transaction), arg0)
} }
// Transactions mocks base method. // Transactions mocks base method.