You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
348 lines
11 KiB
348 lines
11 KiB
package integration |
|
|
|
import ( |
|
"context" |
|
"encoding/json" |
|
"fmt" |
|
"os" |
|
"path/filepath" |
|
"testing" |
|
"time" |
|
|
|
"autostore/internal/domain/entities" |
|
"autostore/internal/domain/specifications" |
|
"autostore/internal/domain/value_objects" |
|
"autostore/internal/infrastructure/repositories" |
|
"github.com/stretchr/testify/assert" |
|
) |
|
|
|
const ( |
|
ITEM_ID_1 = "00000000-0000-0000-0000-000000000001" |
|
ITEM_ID_2 = "00000000-0000-0000-0000-000000000002" |
|
ITEM_ID_3 = "00000000-0000-0000-0000-000000000003" |
|
EXPIRED_ID = "00000000-0000-0000-0000-000000000004" |
|
VALID_ID = "00000000-0000-0000-0000-000000000005" |
|
NON_EXISTENT_ID = "00000000-0000-0000-0000-000000000099" |
|
ITEM_NAME_1 = "Test Item 1" |
|
ITEM_NAME_2 = "Test Item 2" |
|
ITEM_NAME_3 = "Test Item 3" |
|
EXPIRED_NAME = "Expired Item" |
|
VALID_NAME = "Valid Item" |
|
ORDER_URL_1 = "http://example.com/order1" |
|
ORDER_URL_2 = "http://example.com/order2" |
|
ORDER_URL_3 = "http://example.com/order3" |
|
EXPIRED_ORDER_URL = "http://example.com/expired-order" |
|
VALID_ORDER_URL = "http://example.com/valid-order" |
|
USER_ID_1 = "10000000-0000-0000-0000-000000000001" |
|
USER_ID_2 = "10000000-0000-0000-0000-000000000002" |
|
USER_ID = "10000000-0000-0000-0000-000000000003" |
|
MOCKED_NOW = "2023-01-01T12:00:00Z" |
|
DATE_FORMAT = time.RFC3339 |
|
) |
|
|
|
type mockLogger struct { |
|
infoLogs []string |
|
errorLogs []string |
|
} |
|
|
|
func (m *mockLogger) Info(ctx context.Context, msg string, args ...any) { |
|
m.infoLogs = append(m.infoLogs, fmt.Sprintf(msg, args...)) |
|
} |
|
|
|
func (m *mockLogger) Error(ctx context.Context, msg string, args ...any) { |
|
m.errorLogs = append(m.errorLogs, fmt.Sprintf(msg, args...)) |
|
} |
|
|
|
func (m *mockLogger) Debug(ctx context.Context, msg string, args ...any) { |
|
// Debug implementation - can be empty for tests |
|
} |
|
|
|
func (m *mockLogger) Warn(ctx context.Context, msg string, args ...any) { |
|
// Warn implementation - can be empty for tests |
|
} |
|
|
|
func setupTest(t *testing.T) (string, *repositories.FileItemRepository, *mockLogger, func()) { |
|
testStoragePath := t.TempDir() |
|
logger := &mockLogger{} |
|
repo := repositories.NewFileItemRepository(testStoragePath, logger) |
|
|
|
cleanup := func() { |
|
os.RemoveAll(testStoragePath) |
|
} |
|
|
|
return testStoragePath, repo, logger, cleanup |
|
} |
|
|
|
func createTestItem(id, name string, expiration time.Time, orderURL, userID string) *entities.ItemEntity { |
|
itemID, _ := value_objects.NewItemID(id) |
|
userIDVO, _ := value_objects.NewUserID(userID) |
|
expirationVO, _ := value_objects.NewExpirationDate(expiration) |
|
item, _ := entities.NewItem(itemID, name, expirationVO, orderURL, userIDVO) |
|
return item |
|
} |
|
|
|
func createTestItem1() *entities.ItemEntity { |
|
expiration, _ := time.Parse(DATE_FORMAT, "2025-01-01T12:00:00Z") |
|
return createTestItem(ITEM_ID_1, ITEM_NAME_1, expiration, ORDER_URL_1, USER_ID_1) |
|
} |
|
|
|
func createTestItem2() *entities.ItemEntity { |
|
expiration, _ := time.Parse(DATE_FORMAT, "2025-01-02T12:00:00Z") |
|
return createTestItem(ITEM_ID_2, ITEM_NAME_2, expiration, ORDER_URL_2, USER_ID_2) |
|
} |
|
|
|
func createTestItem3() *entities.ItemEntity { |
|
expiration, _ := time.Parse(DATE_FORMAT, "2025-01-03T12:00:00Z") |
|
return createTestItem(ITEM_ID_3, ITEM_NAME_3, expiration, ORDER_URL_3, USER_ID_1) |
|
} |
|
|
|
func createExpiredItem() *entities.ItemEntity { |
|
expiration, _ := time.Parse(DATE_FORMAT, "2022-01-01T12:00:00Z") // Past date |
|
return createTestItem(EXPIRED_ID, EXPIRED_NAME, expiration, EXPIRED_ORDER_URL, USER_ID) |
|
} |
|
|
|
func createValidItem() *entities.ItemEntity { |
|
expiration, _ := time.Parse(DATE_FORMAT, "2024-01-01T12:00:00Z") // Future date |
|
return createTestItem(VALID_ID, VALID_NAME, expiration, VALID_ORDER_URL, USER_ID) |
|
} |
|
|
|
func createExpiredItemForUser1() *entities.ItemEntity { |
|
expiration, _ := time.Parse(DATE_FORMAT, "2022-01-01T12:00:00Z") // Past date |
|
return createTestItem(ITEM_ID_1, ITEM_NAME_1, expiration, ORDER_URL_1, USER_ID_1) |
|
} |
|
|
|
func createValidItemForUser1() *entities.ItemEntity { |
|
expiration, _ := time.Parse(DATE_FORMAT, "2024-01-01T12:00:00Z") // Future date |
|
return createTestItem(ITEM_ID_2, ITEM_NAME_2, expiration, ORDER_URL_2, USER_ID_1) |
|
} |
|
|
|
func createExpiredItemForUser2() *entities.ItemEntity { |
|
expiration, _ := time.Parse(DATE_FORMAT, "2022-01-01T12:00:00Z") // Past date |
|
return createTestItem(ITEM_ID_3, ITEM_NAME_3, expiration, ORDER_URL_3, USER_ID_2) |
|
} |
|
|
|
func TestWhenItemIsSavedThenFileIsCreated(t *testing.T) { |
|
testStoragePath, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
item := createTestItem1() |
|
|
|
err := repo.Save(context.Background(), item) |
|
assert.NoError(t, err) |
|
|
|
filePath := filepath.Join(testStoragePath, "items.json") |
|
assert.FileExists(t, filePath) |
|
|
|
data, err := os.ReadFile(filePath) |
|
assert.NoError(t, err) |
|
|
|
var items map[string]*entities.ItemEntity |
|
err = json.Unmarshal(data, &items) |
|
assert.NoError(t, err) |
|
assert.Len(t, items, 1) |
|
|
|
savedItem := items[item.GetID().String()] |
|
assert.NotNil(t, savedItem) |
|
assert.Equal(t, item.GetID().String(), savedItem.GetID().String()) |
|
assert.Equal(t, item.GetName(), savedItem.GetName()) |
|
assert.Equal(t, item.GetOrderURL(), savedItem.GetOrderURL()) |
|
assert.Equal(t, item.GetUserID().String(), savedItem.GetUserID().String()) |
|
} |
|
|
|
func TestWhenItemExistsThenFindByIDReturnsItem(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
item := createTestItem1() |
|
err := repo.Save(context.Background(), item) |
|
assert.NoError(t, err) |
|
|
|
itemID, _ := value_objects.NewItemID(ITEM_ID_1) |
|
foundItem, err := repo.FindByID(context.Background(), itemID) |
|
assert.NoError(t, err) |
|
assert.NotNil(t, foundItem) |
|
assert.Equal(t, item.GetID().String(), foundItem.GetID().String()) |
|
assert.Equal(t, item.GetName(), foundItem.GetName()) |
|
} |
|
|
|
func TestWhenItemDoesNotExistThenFindByIDReturnsNil(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
nonExistentID, _ := value_objects.NewItemID(NON_EXISTENT_ID) |
|
foundItem, err := repo.FindByID(context.Background(), nonExistentID) |
|
assert.Error(t, err) |
|
assert.Contains(t, err.Error(), "not found") |
|
assert.Nil(t, foundItem) |
|
} |
|
|
|
func TestWhenUserHasMultipleItemsThenFindByUserIDReturnsAllUserItems(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
item1 := createTestItem1() |
|
item2 := createTestItem2() |
|
item3 := createTestItem3() |
|
|
|
repo.Save(context.Background(), item1) |
|
repo.Save(context.Background(), item2) |
|
repo.Save(context.Background(), item3) |
|
|
|
userID, _ := value_objects.NewUserID(USER_ID_1) |
|
userItems, err := repo.FindByUserID(context.Background(), userID) |
|
assert.NoError(t, err) |
|
assert.Len(t, userItems, 2) |
|
|
|
itemIDs := make([]string, len(userItems)) |
|
for i, item := range userItems { |
|
itemIDs[i] = item.GetID().String() |
|
} |
|
assert.Contains(t, itemIDs, ITEM_ID_1) |
|
assert.Contains(t, itemIDs, ITEM_ID_3) |
|
} |
|
|
|
func TestWhenItemIsDeletedThenItIsNoLongerFound(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
item := createTestItem1() |
|
err := repo.Save(context.Background(), item) |
|
assert.NoError(t, err) |
|
|
|
itemID, _ := value_objects.NewItemID(ITEM_ID_1) |
|
err = repo.Delete(context.Background(), itemID) |
|
assert.NoError(t, err) |
|
|
|
foundItem, err := repo.FindByID(context.Background(), itemID) |
|
assert.Error(t, err) |
|
assert.Contains(t, err.Error(), "not found") |
|
assert.Nil(t, foundItem) |
|
} |
|
|
|
func TestWhenNonExistentItemIsDeletedThenErrorIsReturned(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
nonExistentID, _ := value_objects.NewItemID(NON_EXISTENT_ID) |
|
err := repo.Delete(context.Background(), nonExistentID) |
|
assert.Error(t, err) |
|
assert.Contains(t, err.Error(), fmt.Sprintf("item with ID %s not found", NON_EXISTENT_ID)) |
|
} |
|
|
|
func TestWhenFilteringByExpirationThenOnlyExpiredItemsAreReturned(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
expiredItem := createExpiredItem() |
|
validItem := createValidItem() |
|
|
|
repo.Save(context.Background(), expiredItem) |
|
repo.Save(context.Background(), validItem) |
|
|
|
now, _ := time.Parse(DATE_FORMAT, MOCKED_NOW) |
|
expirationSpec := specifications.NewItemExpirationSpec() |
|
spec := expirationSpec.GetSpec(now) |
|
|
|
filteredItems, err := repo.FindWhere(context.Background(), spec) |
|
assert.NoError(t, err) |
|
assert.Len(t, filteredItems, 1) |
|
assert.Equal(t, expiredItem.GetID().String(), filteredItems[0].GetID().String()) |
|
} |
|
|
|
func TestWhenFilteringByUserIDThenOnlyUserItemsAreReturned(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
item1 := createTestItem1() |
|
item2 := createTestItem2() |
|
item3 := createTestItem3() |
|
|
|
repo.Save(context.Background(), item1) |
|
repo.Save(context.Background(), item2) |
|
repo.Save(context.Background(), item3) |
|
|
|
userID, _ := value_objects.NewUserID(USER_ID_1) |
|
spec := specifications.NewSimpleSpecification[*entities.ItemEntity](specifications.Eq("userID", userID)) |
|
|
|
userItems, err := repo.FindWhere(context.Background(), spec) |
|
assert.NoError(t, err) |
|
assert.Len(t, userItems, 2) |
|
|
|
itemIDs := make([]string, len(userItems)) |
|
for i, item := range userItems { |
|
itemIDs[i] = item.GetID().String() |
|
} |
|
assert.Contains(t, itemIDs, ITEM_ID_1) |
|
assert.Contains(t, itemIDs, ITEM_ID_3) |
|
} |
|
|
|
func TestWhenUsingComplexFilterThenOnlyMatchingItemsAreReturned(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
item1 := createExpiredItemForUser1() |
|
item2 := createValidItemForUser1() |
|
item3 := createExpiredItemForUser2() |
|
|
|
repo.Save(context.Background(), item1) |
|
repo.Save(context.Background(), item2) |
|
repo.Save(context.Background(), item3) |
|
|
|
now, _ := time.Parse(DATE_FORMAT, MOCKED_NOW) |
|
userID, _ := value_objects.NewUserID(USER_ID_1) |
|
userSpec := specifications.NewSimpleSpecification[*entities.ItemEntity](specifications.Eq("userID", userID)) |
|
expirationSpec := specifications.NewItemExpirationSpec() |
|
expirationSpecWithTime := expirationSpec.GetSpec(now) |
|
complexSpec := userSpec.And(expirationSpecWithTime) |
|
|
|
filteredItems, err := repo.FindWhere(context.Background(), complexSpec) |
|
assert.NoError(t, err) |
|
assert.Len(t, filteredItems, 1) |
|
assert.Equal(t, item1.GetID().String(), filteredItems[0].GetID().String()) |
|
} |
|
|
|
func TestWhenCheckingExistenceOfExistingItemThenReturnsTrue(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
item := createTestItem1() |
|
err := repo.Save(context.Background(), item) |
|
assert.NoError(t, err) |
|
|
|
itemID, _ := value_objects.NewItemID(ITEM_ID_1) |
|
exists, err := repo.Exists(context.Background(), itemID) |
|
assert.NoError(t, err) |
|
assert.True(t, exists) |
|
} |
|
|
|
func TestWhenCheckingExistenceOfNonExistentItemThenReturnsFalse(t *testing.T) { |
|
_, repo, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
nonExistentID, _ := value_objects.NewItemID(NON_EXISTENT_ID) |
|
exists, err := repo.Exists(context.Background(), nonExistentID) |
|
assert.NoError(t, err) |
|
assert.False(t, exists) |
|
} |
|
|
|
func TestWhenDifferentRepositoryInstancesShareSameFile(t *testing.T) { |
|
testStoragePath, _, _, cleanup := setupTest(t) |
|
defer cleanup() |
|
|
|
logger := &mockLogger{} |
|
repo1 := repositories.NewFileItemRepository(testStoragePath, logger) |
|
repo2 := repositories.NewFileItemRepository(testStoragePath, logger) |
|
|
|
item := createTestItem1() |
|
err := repo1.Save(context.Background(), item) |
|
assert.NoError(t, err) |
|
|
|
itemID, _ := value_objects.NewItemID(ITEM_ID_1) |
|
foundItem, err := repo2.FindByID(context.Background(), itemID) |
|
assert.NoError(t, err) |
|
assert.NotNil(t, foundItem) |
|
assert.Equal(t, item.GetID().String(), foundItem.GetID().String()) |
|
assert.Equal(t, item.GetName(), foundItem.GetName()) |
|
assert.Equal(t, item.GetOrderURL(), foundItem.GetOrderURL()) |
|
assert.Equal(t, item.GetUserID().String(), foundItem.GetUserID().String()) |
|
} |