Merge pull request #14859 from backstage/search/test-pipeline-improvements
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend-node': patch
|
||||
---
|
||||
|
||||
Use of `TestPipeline.withSubject()` is now deprecated. Instead, use the `fromCollator`, `fromDecorator`, or `fromIndexer` static methods to instantiate a test pipeline. You may also use the class' `withCollator`, `withDecorator`, and `withIndexer` instance methods to build test pipelines that consist of multiple test subjects.
|
||||
@@ -114,14 +114,14 @@ describe('DefaultCatalogCollatorFactory', () => {
|
||||
});
|
||||
|
||||
it('fetches from the configured catalog service', async () => {
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('catalog');
|
||||
expect(documents).toHaveLength(expectedEntities.length);
|
||||
});
|
||||
|
||||
it('maps a returned entity to an expected CatalogEntityDocument', async () => {
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
|
||||
expect(documents[0]).toMatchObject({
|
||||
@@ -159,7 +159,7 @@ describe('DefaultCatalogCollatorFactory', () => {
|
||||
});
|
||||
collator = await factory.getCollator();
|
||||
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
expect(documents[0]).toMatchObject({
|
||||
location: '/software/test-entity',
|
||||
@@ -177,7 +177,7 @@ describe('DefaultCatalogCollatorFactory', () => {
|
||||
});
|
||||
collator = await factory.getCollator();
|
||||
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
|
||||
// The simulated 'Foo,Bar' filter should return in an empty list
|
||||
@@ -191,7 +191,7 @@ describe('DefaultCatalogCollatorFactory', () => {
|
||||
});
|
||||
collator = await factory.getCollator();
|
||||
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
|
||||
expect(documents).toHaveLength(expectedEntities.length);
|
||||
|
||||
+3
-3
@@ -137,7 +137,7 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
},
|
||||
];
|
||||
|
||||
await TestPipeline.withSubject(indexer).withDocuments(documents).execute();
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
|
||||
// Older indices should have been queried for.
|
||||
expect(catSpy).toHaveBeenCalled();
|
||||
@@ -184,7 +184,7 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
text: range(2000).join(', '),
|
||||
}));
|
||||
|
||||
await TestPipeline.withSubject(indexer).withDocuments(documents).execute();
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
|
||||
// Ensure multiple bulk requests were made.
|
||||
expect(bulkSpy).toHaveBeenCalledTimes(2);
|
||||
@@ -221,7 +221,7 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
catSpy,
|
||||
);
|
||||
|
||||
await TestPipeline.withSubject(indexer).withDocuments(documents).execute();
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
|
||||
// Final deletion shouldn't be called.
|
||||
expect(deleteSpy).not.toHaveBeenCalled();
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('PgSearchEngineIndexer', () => {
|
||||
},
|
||||
];
|
||||
|
||||
await TestPipeline.withSubject(indexer).withDocuments(documents).execute();
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
|
||||
expect(database.getTransaction).toHaveBeenCalledTimes(1);
|
||||
expect(database.prepareInsert).toHaveBeenCalledTimes(1);
|
||||
@@ -73,7 +73,7 @@ describe('PgSearchEngineIndexer', () => {
|
||||
location: `location-${i}`,
|
||||
}));
|
||||
|
||||
await TestPipeline.withSubject(indexer).withDocuments(documents).execute();
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
|
||||
expect(database.getTransaction).toHaveBeenCalledTimes(1);
|
||||
expect(database.prepareInsert).toHaveBeenCalledTimes(1);
|
||||
@@ -92,7 +92,7 @@ describe('PgSearchEngineIndexer', () => {
|
||||
];
|
||||
|
||||
database.prepareInsert.mockRejectedValueOnce(expectedError);
|
||||
const result = await TestPipeline.withSubject(indexer)
|
||||
const result = await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(documents)
|
||||
.execute();
|
||||
|
||||
@@ -114,7 +114,7 @@ describe('PgSearchEngineIndexer', () => {
|
||||
];
|
||||
|
||||
database.insertDocuments.mockRejectedValueOnce(expectedError);
|
||||
const result = await TestPipeline.withSubject(indexer)
|
||||
const result = await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(documents)
|
||||
.execute();
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('PgSearchEngineIndexer', () => {
|
||||
];
|
||||
|
||||
database.completeInsert.mockRejectedValueOnce(expectedError);
|
||||
const result = await TestPipeline.withSubject(indexer)
|
||||
const result = await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(documents)
|
||||
.execute();
|
||||
|
||||
|
||||
@@ -173,7 +173,14 @@ export type ScheduleTaskParameters = {
|
||||
// @public
|
||||
export class TestPipeline {
|
||||
execute(): Promise<TestPipelineResult>;
|
||||
static fromCollator(collator: Readable): TestPipeline;
|
||||
static fromDecorator(decorator: Transform): TestPipeline;
|
||||
static fromIndexer(indexer: Writable): TestPipeline;
|
||||
withCollator(collator: Readable): this;
|
||||
withDecorator(decorator: Transform): this;
|
||||
withDocuments(documents: IndexableDocument[]): TestPipeline;
|
||||
withIndexer(indexer: Writable): this;
|
||||
// @deprecated
|
||||
static withSubject(subject: Readable | Transform | Writable): TestPipeline;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ describe('DefaultCatalogCollatorFactory', () => {
|
||||
});
|
||||
|
||||
const collator = await factory.getCollator();
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
|
||||
expect(documents).toHaveLength(2);
|
||||
|
||||
@@ -381,7 +381,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -412,7 +412,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -452,7 +452,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -495,7 +495,7 @@ describe('LunrSearchEngine', () => {
|
||||
inspectableSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -544,7 +544,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -583,7 +583,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -623,7 +623,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -663,7 +663,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -708,7 +708,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -763,10 +763,10 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index-2',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer1)
|
||||
await TestPipeline.fromIndexer(indexer1)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
await TestPipeline.withSubject(indexer2)
|
||||
await TestPipeline.fromIndexer(indexer2)
|
||||
.withDocuments(mockDocuments2)
|
||||
.execute();
|
||||
|
||||
@@ -811,7 +811,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -871,14 +871,14 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
const indexer2 = await getActualIndexer(
|
||||
testLunrSearchEngine,
|
||||
'test-index-2',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer2)
|
||||
await TestPipeline.fromIndexer(indexer2)
|
||||
.withDocuments(mockDocuments2)
|
||||
.execute();
|
||||
|
||||
@@ -924,7 +924,7 @@ describe('LunrSearchEngine', () => {
|
||||
testLunrSearchEngine,
|
||||
'test-index',
|
||||
);
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -974,7 +974,7 @@ describe('LunrSearchEngine', () => {
|
||||
}));
|
||||
|
||||
const indexer = await getActualIndexer(testLunrSearchEngine, 'test-index');
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -1112,7 +1112,7 @@ describe('stopword testing', () => {
|
||||
|
||||
const indexer = await getActualIndexer(testLunrSearchEngine, 'test-index');
|
||||
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -1149,7 +1149,7 @@ describe('stopword testing', () => {
|
||||
|
||||
const indexer = await getActualIndexer(testLunrSearchEngine, 'test-index');
|
||||
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
@@ -1186,7 +1186,7 @@ describe('stopword testing', () => {
|
||||
|
||||
const indexer = await getActualIndexer(testLunrSearchEngine, 'test-index');
|
||||
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments(mockDocuments)
|
||||
.execute();
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('LunrSearchEngineIndexer', () => {
|
||||
},
|
||||
];
|
||||
|
||||
await TestPipeline.withSubject(indexer).withDocuments(documents).execute();
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
|
||||
expect(lunrBuilderAddSpy).toHaveBeenCalledWith(documents[0]);
|
||||
});
|
||||
@@ -70,7 +70,7 @@ describe('LunrSearchEngineIndexer', () => {
|
||||
location: `location-${i}`,
|
||||
}));
|
||||
|
||||
await TestPipeline.withSubject(indexer).withDocuments(documents).execute();
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
expect(lunrBuilderAddSpy).toHaveBeenCalledTimes(350);
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('LunrSearchEngineIndexer', () => {
|
||||
},
|
||||
];
|
||||
|
||||
await TestPipeline.withSubject(indexer).withDocuments(documents).execute();
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
|
||||
// Builder ref should be set to location (and only once).
|
||||
expect(lunrBuilderRefSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('BatchSearchEngineIndexer', () => {
|
||||
|
||||
it('should work end-to-end', async () => {
|
||||
const indexer = new ConcreteBatchIndexer({ batchSize: 1 });
|
||||
await TestPipeline.withSubject(indexer)
|
||||
await TestPipeline.fromIndexer(indexer)
|
||||
.withDocuments([document, document, document])
|
||||
.execute();
|
||||
expect(indexSpy).toHaveBeenCalledTimes(3);
|
||||
|
||||
@@ -54,7 +54,7 @@ describe('DecoratorBase', () => {
|
||||
}));
|
||||
|
||||
const decorator = new ConcreteDecorator();
|
||||
const { documents } = await TestPipeline.withSubject(decorator)
|
||||
const { documents } = await TestPipeline.fromDecorator(decorator)
|
||||
.withDocuments([document, document, document])
|
||||
.execute();
|
||||
|
||||
@@ -68,7 +68,7 @@ describe('DecoratorBase', () => {
|
||||
decorateSpy.mockResolvedValue(undefined);
|
||||
|
||||
const decorator = new ConcreteDecorator();
|
||||
const { documents } = await TestPipeline.withSubject(decorator)
|
||||
const { documents } = await TestPipeline.fromDecorator(decorator)
|
||||
.withDocuments([document, document, document])
|
||||
.execute();
|
||||
|
||||
@@ -82,7 +82,7 @@ describe('DecoratorBase', () => {
|
||||
});
|
||||
|
||||
const decorator = new ConcreteDecorator();
|
||||
const { documents } = await TestPipeline.withSubject(decorator)
|
||||
const { documents } = await TestPipeline.fromDecorator(decorator)
|
||||
.withDocuments([document, document, document])
|
||||
.execute();
|
||||
|
||||
|
||||
@@ -37,6 +37,35 @@ export type TestPipelineResult = {
|
||||
|
||||
/**
|
||||
* Test utility for Backstage Search collators, decorators, and indexers.
|
||||
*
|
||||
* @example
|
||||
* An example test checking that a collator provides expected documents.
|
||||
* ```
|
||||
* it('provides expected documents', async () => {
|
||||
* const testSubject = await yourCollatorFactory.getCollator();
|
||||
* const pipeline = TestPipeline.fromCollator(testSubject);
|
||||
*
|
||||
* const { documents } = await pipeline.execute();
|
||||
*
|
||||
* expect(documents).toHaveLength(2);
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* An example test checking that a decorator behaves as expected.
|
||||
* ```
|
||||
* it('filters private documents', async () => {
|
||||
* const testSubject = await yourDecoratorFactory.getDecorator();
|
||||
* const pipeline = TestPipeline
|
||||
* .fromDecorator(testSubject)
|
||||
* .withDocuments([{ title: 'Private', location: '/private', text: '' }]);
|
||||
*
|
||||
* const { documents } = await pipeline.execute();
|
||||
*
|
||||
* expect(documents).toHaveLength(0);
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class TestPipeline {
|
||||
@@ -60,6 +89,9 @@ export class TestPipeline {
|
||||
|
||||
/**
|
||||
* Provide the collator, decorator, or indexer to be tested.
|
||||
*
|
||||
* @deprecated Use `fromCollator`, `fromDecorator` or `fromIndexer` static
|
||||
* methods to create a test pipeline instead.
|
||||
*/
|
||||
static withSubject(subject: Readable | Transform | Writable) {
|
||||
if (subject instanceof Transform) {
|
||||
@@ -79,6 +111,51 @@ export class TestPipeline {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test pipeline given a collator you want to test.
|
||||
*/
|
||||
static fromCollator(collator: Readable) {
|
||||
return new TestPipeline({ collator });
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a collator to the test pipeline.
|
||||
*/
|
||||
withCollator(collator: Readable): this {
|
||||
this.collator = collator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test pipeline given a decorator you want to test.
|
||||
*/
|
||||
static fromDecorator(decorator: Transform) {
|
||||
return new TestPipeline({ decorator });
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a decorator to the test pipeline.
|
||||
*/
|
||||
withDecorator(decorator: Transform): this {
|
||||
this.decorator = decorator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test pipeline given an indexer you want to test.
|
||||
*/
|
||||
static fromIndexer(indexer: Writable) {
|
||||
return new TestPipeline({ indexer });
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an indexer to the test pipeline.
|
||||
*/
|
||||
withIndexer(indexer: Writable): this {
|
||||
this.indexer = indexer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide documents for testing decorators and indexers.
|
||||
*/
|
||||
|
||||
@@ -145,7 +145,7 @@ describe('DefaultTechDocsCollatorFactory', () => {
|
||||
});
|
||||
|
||||
it('fetches from the configured catalog and tech docs services', async () => {
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('catalog');
|
||||
expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('techdocs');
|
||||
@@ -153,7 +153,7 @@ describe('DefaultTechDocsCollatorFactory', () => {
|
||||
});
|
||||
|
||||
it('should create documents for each tech docs search index', async () => {
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
const entity = expectedEntities[0];
|
||||
documents.forEach((document, idx) => {
|
||||
@@ -182,7 +182,7 @@ describe('DefaultTechDocsCollatorFactory', () => {
|
||||
});
|
||||
collator = await factory.getCollator();
|
||||
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
|
||||
expect(documents[0]).toMatchObject({
|
||||
@@ -200,7 +200,7 @@ describe('DefaultTechDocsCollatorFactory', () => {
|
||||
});
|
||||
collator = await factory.getCollator();
|
||||
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
|
||||
// Only 1 entity with TechDocs configured multiplied by 3 pages.
|
||||
@@ -222,7 +222,7 @@ describe('DefaultTechDocsCollatorFactory', () => {
|
||||
});
|
||||
|
||||
it('should create documents for each tech docs search index', async () => {
|
||||
const pipeline = TestPipeline.withSubject(collator);
|
||||
const pipeline = TestPipeline.fromCollator(collator);
|
||||
const { documents } = await pipeline.execute();
|
||||
const entity = expectedEntities[0];
|
||||
documents.forEach((document, idx) => {
|
||||
|
||||
Reference in New Issue
Block a user