diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 3ae4a6c1e2..92d581d930 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -49,7 +49,12 @@ describe.each([ let table: Table let tableId: string - afterAll(setup.afterAll) + afterAll(async () => { + if (dsProvider) { + await dsProvider.stopContainer() + } + setup.afterAll() + }) beforeAll(async () => { await config.init() @@ -521,20 +526,17 @@ describe.each([ const rowUsage = await getRowUsage() const queryUsage = await getQueryUsage() - const res = await config.api.row.patch(table._id!, { + const row = await config.api.row.patch(table._id!, { _id: existing._id!, _rev: existing._rev!, tableId: table._id!, name: "Updated Name", }) - expect((res as any).res.statusMessage).toEqual( - `${table.name} updated successfully.` - ) - expect(res.body.name).toEqual("Updated Name") - expect(res.body.description).toEqual(existing.description) + expect(row.name).toEqual("Updated Name") + expect(row.description).toEqual(existing.description) - const savedRow = await loadRow(res.body._id, table._id!) + const savedRow = await loadRow(row._id!, table._id!) expect(savedRow.body.description).toEqual(existing.description) expect(savedRow.body.name).toEqual("Updated Name") diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index c239c596fe..4743bca814 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -492,6 +492,67 @@ describe("/tables", () => { } }) + it("should succeed when the row is created from the other side of the relationship", async () => { + // We found a bug just after releasing this feature where if the row was created from the + // users table, not the table linking to it, the migration would succeed but lose the data. + // This happened because the order of the documents in the link was reversed. + const table = await config.api.table.create({ + name: "table", + type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, + sourceType: TableSourceType.INTERNAL, + schema: { + "user relationship": { + type: FieldType.LINK, + fieldName: "test", + name: "user relationship", + constraints: { + type: "array", + presence: false, + }, + relationshipType: RelationshipType.MANY_TO_ONE, + tableId: InternalTable.USER_METADATA, + }, + }, + }) + + let testRow = await config.api.row.save(table._id!, {}) + + await Promise.all( + users.map(u => + config.api.row.patch(InternalTable.USER_METADATA, { + tableId: InternalTable.USER_METADATA, + _rev: u._rev!, + _id: u._id!, + test: [testRow], + }) + ) + ) + + await config.api.table.migrate(table._id!, { + oldColumn: table.schema["user relationship"], + newColumn: { + name: "user column", + type: FieldType.BB_REFERENCE, + subtype: FieldSubtype.USERS, + }, + }) + + const migratedTable = await config.api.table.get(table._id!) + expect(migratedTable.schema["user column"]).toBeDefined() + expect(migratedTable.schema["user relationship"]).not.toBeDefined() + + const resp = await config.api.row.get(table._id!, testRow._id!) + const migratedRow = resp.body as Row + + expect(migratedRow["user column"]).toBeDefined() + expect(migratedRow["user relationship"]).not.toBeDefined() + expect(migratedRow["user column"]).toHaveLength(3) + expect(migratedRow["user column"].map((u: Row) => u._id)).toEqual( + expect.arrayContaining(users.map(u => u._id)) + ) + }) + it("should successfully migrate a many-to-many user relationship to a users column", async () => { const table = await config.api.table.create({ name: "table", diff --git a/packages/server/src/tests/utilities/api/row.ts b/packages/server/src/tests/utilities/api/row.ts index bb880bb7da..20b1d6f9ee 100644 --- a/packages/server/src/tests/utilities/api/row.ts +++ b/packages/server/src/tests/utilities/api/row.ts @@ -55,7 +55,13 @@ export class RowAPI extends TestAPI { .send(row) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(expectStatus) + if (resp.status !== expectStatus) { + throw new Error( + `Expected status ${expectStatus} but got ${ + resp.status + }, body: ${JSON.stringify(resp.body)}` + ) + } return resp.body as Row } @@ -77,13 +83,20 @@ export class RowAPI extends TestAPI { sourceId: string, row: PatchRowRequest, { expectStatus } = { expectStatus: 200 } - ) => { - return this.request + ): Promise => { + let resp = await this.request .patch(`/api/${sourceId}/rows`) .send(row) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(expectStatus) + if (resp.status !== expectStatus) { + throw new Error( + `Expected status ${expectStatus} but got ${ + resp.status + }, body: ${JSON.stringify(resp.body)}` + ) + } + return resp.body as Row } delete = async (