Fix MySQL existence check and add router/store tests
- MySQL UPDATE returns 0 affected rows when values are unchanged, so detect existence via SELECT within the transaction instead. - Add integration tests for PUT /locations/:id in createRouter.test.ts. - Add changeset for catalog-react catalogApiMock update. Signed-off-by: Fredrik Adelöw <freben@spotify.com> Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Updated `catalogApiMock` to include the new `updateLocation` method stub, keeping it in sync with the `CatalogApi` interface.
|
||||
@@ -210,16 +210,19 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
|
||||
throw new Error('location store is not initialized');
|
||||
}
|
||||
|
||||
// MySQL doesn't support UPDATE ... RETURNING, so we fall back to checking
|
||||
// the affected row count and then doing a separate SELECT.
|
||||
// MySQL doesn't support UPDATE ... RETURNING. MySQL also reports 0 affected
|
||||
// rows when the new values are identical to the old ones, so we can't rely
|
||||
// on the row count to detect existence. Instead we SELECT to check existence
|
||||
// first and then UPDATE inside a transaction.
|
||||
let row: DbLocationsRow | undefined;
|
||||
if (this.db.client.config.client.includes('mysql')) {
|
||||
await this.db.transaction(async tx => {
|
||||
const count = await tx<DbLocationsRow>('locations')
|
||||
.where({ id })
|
||||
.update({ type: location.type, target: location.target });
|
||||
if (Number(count) > 0) {
|
||||
[row] = await tx<DbLocationsRow>('locations').where({ id }).select();
|
||||
[row] = await tx<DbLocationsRow>('locations').where({ id }).select();
|
||||
if (row) {
|
||||
await tx<DbLocationsRow>('locations')
|
||||
.where({ id })
|
||||
.update({ type: location.type, target: location.target });
|
||||
row = { ...row, type: location.type, target: location.target };
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1175,6 +1175,43 @@ describe('createRouter readonly disabled', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /locations/:id', () => {
|
||||
it('rejects malformed body', async () => {
|
||||
const response = await request(app)
|
||||
.put('/locations/foo')
|
||||
.send({ typez: 'url', target: 'https://example.com' });
|
||||
|
||||
expect(locationService.updateLocation).not.toHaveBeenCalled();
|
||||
expect(response.status).toEqual(400);
|
||||
});
|
||||
|
||||
it('updates the location and returns it', async () => {
|
||||
const spec: LocationInput = {
|
||||
type: 'url',
|
||||
target: 'https://example.com/new',
|
||||
};
|
||||
|
||||
locationService.updateLocation.mockResolvedValue({
|
||||
id: 'foo',
|
||||
...spec,
|
||||
entityRef: 'location:default/generated-foo',
|
||||
});
|
||||
|
||||
const response = await request(app).put('/locations/foo').send(spec);
|
||||
|
||||
expect(locationService.updateLocation).toHaveBeenCalledTimes(1);
|
||||
expect(locationService.updateLocation).toHaveBeenCalledWith('foo', spec, {
|
||||
credentials: mockCredentials.user(),
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
id: 'foo',
|
||||
...spec,
|
||||
entityRef: 'location:default/generated-foo',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /locations/by-entity/:kind/:namespace/:name', () => {
|
||||
it('happy path: gets location by entity ref', async () => {
|
||||
const location: Location = {
|
||||
|
||||
Reference in New Issue
Block a user