Overview
Refactor BackfillEmbeddingsServiceTest and SearchContentServiceTest to use Testcontainers with real production code instead of Mockito mocks.
Priority: HIGH
Estimated Time: Week 2
Depends On: #528 (Phase 1)
Context
Replace mock-based testing with integration tests using:
- REAL
ContentPersistenceAdapter and ArcadeContentRepository
- REAL ArcadeDB with actual vector index
FakeEmbeddingGenerator for deterministic embeddings
- State-based assertions instead of mock
verify() statements
Benefits
- ✅ Tests validate actual database behavior, not mock interactions
- ✅ Catches SQL errors, schema issues, and mapping bugs
- ✅ Tests survive refactoring (no brittle mock setups)
- ✅ More readable with Given/When/Then structure
- ✅ Validates real vector search with LSM_TREE index
Tasks
2.1 Refactor BackfillEmbeddingsServiceTest
File: src/test/java/it/robfrank/linklift/application/domain/service/BackfillEmbeddingsServiceTest.java
Changes:
- Extend
ArcadeDbTestBase instead of using @ExtendWith(MockitoExtension.class)
- Replace mocked
LoadContentPort and SaveContentPort with real repository
- Replace mocked
EmbeddingGenerator with FakeEmbeddingGenerator
- Update all 11 tests to:
- Use
repository.saveContent() to set up test data
- Verify actual database state with
repository.findContentById()
- Remove all
verify() statements
- Use Awaitility instead of
Thread.sleep() where possible
Example transformation:
// Before (with mocks)
when(loadContentPort.findContentsWithoutEmbeddings(100))
.thenReturn(List.of(content))
.thenReturn(List.of());
verify(saveContentPort, times(1)).updateContent(argThat(c -> c.embedding() != null));
// After (with Testcontainers)
repository.saveContent(content); // Setup test data in REAL database
backfillEmbeddingsService.backfill();
Thread.sleep(1000);
Content updated = repository.findContentById("id-1").orElseThrow();
assertThat(updated.embedding()).isNotNull().hasSize(384);
2.2 Refactor SearchContentServiceTest
File: src/test/java/it/robfrank/linklift/application/domain/service/SearchContentServiceTest.java
Changes:
- Extend
ArcadeDbTestBase
- Replace mocked
LoadContentPort with real repository
- Replace mocked
EmbeddingGenerator with FakeEmbeddingGenerator
- Update all 21 tests to:
- Save content with embeddings to real database
- Verify actual vector search results using ArcadeDB's LSM_TREE index
- Remove all
verify() statements
- Test actual cosine similarity ranking
Example transformation:
// Before (with mocks)
when(embeddingGenerator.generateEmbedding(query)).thenReturn(queryVector);
when(loadContentPort.findSimilar(queryVector, 10)).thenReturn(List.of(resultContent));
// After (with Testcontainers)
Content ml1 = content.withEmbedding(embeddingGenerator.generateEmbedding("machine learning"));
repository.saveContent(ml1);
List<Content> results = searchContentService.search("machine learning basics", 2);
assertThat(results).extracting(Content::id).containsExactlyInAnyOrder("id-1", "id-2");
2.3 Update Test Structure
For both test classes:
2.4 Validation
Success Criteria
Related
Overview
Refactor
BackfillEmbeddingsServiceTestandSearchContentServiceTestto use Testcontainers with real production code instead of Mockito mocks.Priority: HIGH
Estimated Time: Week 2
Depends On: #528 (Phase 1)
Context
Replace mock-based testing with integration tests using:
ContentPersistenceAdapterandArcadeContentRepositoryFakeEmbeddingGeneratorfor deterministic embeddingsverify()statementsBenefits
Tasks
2.1 Refactor BackfillEmbeddingsServiceTest
File:
src/test/java/it/robfrank/linklift/application/domain/service/BackfillEmbeddingsServiceTest.javaChanges:
ArcadeDbTestBaseinstead of using@ExtendWith(MockitoExtension.class)LoadContentPortandSaveContentPortwith realrepositoryEmbeddingGeneratorwithFakeEmbeddingGeneratorrepository.saveContent()to set up test datarepository.findContentById()verify()statementsThread.sleep()where possibleExample transformation:
2.2 Refactor SearchContentServiceTest
File:
src/test/java/it/robfrank/linklift/application/domain/service/SearchContentServiceTest.javaChanges:
ArcadeDbTestBaseLoadContentPortwith realrepositoryEmbeddingGeneratorwithFakeEmbeddingGeneratorverify()statementsExample transformation:
2.3 Update Test Structure
For both test classes:
@AfterEachto callsuper.tearDownDatabase()and clean upExecutorServiceThread.sleep()with Awaitility where feasibleFIXED_TEST_TIMEfor deterministic dates2.4 Validation
verify()statements remain in service testsSuccess Criteria
ArcadeDbTestBaseRelated
TEST_REFACTORING_PLAN.mdPhase 2 for examples