Multiple implementations of the same back-end application. The aim is to provide quick, side-by-side comparisons of different technologies (languages, frameworks, libraries) while preserving consistent business logic across all implementations.
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

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())
}