package validator import ( "testing" "apskel-pos-be/internal/constants" "apskel-pos-be/internal/contract" "github.com/google/uuid" "github.com/stretchr/testify/require" ) func TestExpenseValidatorCreateRequiresItemName(t *testing.T) { v := NewExpenseValidator() req := &contract.CreateExpenseRequest{ Receiver: "Cashier", TransactionDate: "2026-05-29", CodeNumber: "EXP-001", OutletID: uuid.NewString(), Total: 10000, Items: []contract.CreateExpenseItemRequest{ { ChartOfAccountID: uuid.NewString(), Amount: 10000, }, }, } err, code := v.ValidateCreateExpenseRequest(req) require.Error(t, err) require.Equal(t, constants.MissingFieldErrorCode, code) require.Contains(t, err.Error(), "item 0: item is required") } func TestExpenseValidatorCreateDoesNotRequireHeaderExpenseName(t *testing.T) { v := NewExpenseValidator() req := &contract.CreateExpenseRequest{ Receiver: "Cashier", TransactionDate: "2026-05-29", CodeNumber: "EXP-001", OutletID: uuid.NewString(), Total: 10000, Items: []contract.CreateExpenseItemRequest{ { ChartOfAccountID: uuid.NewString(), Item: "Cleaning supplies", Amount: 10000, }, }, } err, code := v.ValidateCreateExpenseRequest(req) require.NoError(t, err) require.Empty(t, code) } func TestExpenseValidatorCreateAllowsValidOptionalStatus(t *testing.T) { v := NewExpenseValidator() status := "approved" req := &contract.CreateExpenseRequest{ Receiver: "Cashier", TransactionDate: "2026-05-29", CodeNumber: "EXP-001", OutletID: uuid.NewString(), Status: &status, Total: 10000, Items: []contract.CreateExpenseItemRequest{ { ChartOfAccountID: uuid.NewString(), Item: "Cleaning supplies", Amount: 10000, }, }, } err, code := v.ValidateCreateExpenseRequest(req) require.NoError(t, err) require.Empty(t, code) } func TestExpenseValidatorCreateRejectsInvalidStatus(t *testing.T) { v := NewExpenseValidator() status := "cancelled" req := &contract.CreateExpenseRequest{ Receiver: "Cashier", TransactionDate: "2026-05-29", CodeNumber: "EXP-001", OutletID: uuid.NewString(), Status: &status, Total: 10000, Items: []contract.CreateExpenseItemRequest{ { ChartOfAccountID: uuid.NewString(), Item: "Cleaning supplies", Amount: 10000, }, }, } err, code := v.ValidateCreateExpenseRequest(req) require.Error(t, err) require.Equal(t, constants.MalformedFieldErrorCode, code) require.Contains(t, err.Error(), "status must be one of: draft, sent, approved, cancel") } func TestExpenseValidatorUpdateRejectsEmptyItemNameWhenProvided(t *testing.T) { v := NewExpenseValidator() empty := " " req := &contract.UpdateExpenseRequest{ Items: []contract.UpdateExpenseItemRequest{ {Item: &empty}, }, } err, code := v.ValidateUpdateExpenseRequest(req) require.Error(t, err) require.Equal(t, constants.MalformedFieldErrorCode, code) require.Contains(t, err.Error(), "item 0: item cannot be empty") } func TestExpenseValidatorUpdateRejectsInvalidStatus(t *testing.T) { v := NewExpenseValidator() status := "cancelled" req := &contract.UpdateExpenseRequest{Status: &status} err, code := v.ValidateUpdateExpenseRequest(req) require.Error(t, err) require.Equal(t, constants.MalformedFieldErrorCode, code) require.Contains(t, err.Error(), "status must be one of: draft, sent, approved, cancel") } func TestExpenseValidatorListRejectsInvalidStatus(t *testing.T) { v := NewExpenseValidator() req := &contract.ListExpenseRequest{ Page: 1, Limit: 10, Status: "cancelled", } err, code := v.ValidateListExpenseRequest(req) require.Error(t, err) require.Equal(t, constants.MalformedFieldErrorCode, code) require.Contains(t, err.Error(), "status must be one of: draft, sent, approved, cancel") }