Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server relationship tests #1296

Merged
merged 4 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"!src/db/dynamoClient.js",
"!src/utilities/usageQuota.js",
"!src/api/routes/tests/**/*",
"!src/db/tests/**/*",
"!src/tests/**/*",
"!src/automations/tests/**/*",
"!src/utilities/fileProcessor.js",
Expand Down
37 changes: 23 additions & 14 deletions packages/server/src/db/linkedRows/LinkController.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,37 +133,44 @@ class LinkController {
}

/**
* Returns whether the two schemas are equal (in the important parts, not a pure equality check)
* Returns whether the two link schemas are equal (in the important parts, not a pure equality check)
*/
areSchemasEqual(schema1, schema2) {
const compareFields = ["name", "type", "tableId", "fieldName", "autocolumn"]
areLinkSchemasEqual(linkSchema1, linkSchema2) {
const compareFields = [
"name",
"type",
"tableId",
"fieldName",
"autocolumn",
"relationshipType",
]
for (let field of compareFields) {
if (schema1[field] !== schema2[field]) {
if (linkSchema1[field] !== linkSchema2[field]) {
return false
}
}
return true
}

/**
* Given two the field of this table, and the field of the linked table, this makes sure
* Given the link field of this table, and the link field of the linked table, this makes sure
* the state of relationship type is accurate on both.
*/
handleRelationshipType(field, linkedField) {
handleRelationshipType(linkerField, linkedField) {
if (
!field.relationshipType ||
field.relationshipType === RelationshipTypes.MANY_TO_MANY
!linkerField.relationshipType ||
linkerField.relationshipType === RelationshipTypes.MANY_TO_MANY
) {
linkedField.relationshipType = RelationshipTypes.MANY_TO_MANY
// make sure by default all are many to many (if not specified)
field.relationshipType = RelationshipTypes.MANY_TO_MANY
} else if (field.relationshipType === RelationshipTypes.MANY_TO_ONE) {
linkerField.relationshipType = RelationshipTypes.MANY_TO_MANY
} else if (linkerField.relationshipType === RelationshipTypes.MANY_TO_ONE) {
// Ensure that the other side of the relationship is locked to one record
linkedField.relationshipType = RelationshipTypes.ONE_TO_MANY
} else if (field.relationshipType === RelationshipTypes.ONE_TO_MANY) {
} else if (linkerField.relationshipType === RelationshipTypes.ONE_TO_MANY) {
linkedField.relationshipType = RelationshipTypes.MANY_TO_ONE
}
return { field, linkedField }
return { linkerField, linkedField }
}

// all operations here will assume that the table
Expand Down Expand Up @@ -336,6 +343,7 @@ class LinkController {
try {
linkedTable = await this._db.get(field.tableId)
} catch (err) {
/* istanbul ignore next */
continue
}
const fields = this.handleRelationshipType(field, {
Expand All @@ -347,7 +355,7 @@ class LinkController {
})

// update table schema after checking relationship types
schema[fieldName] = fields.field
schema[fieldName] = fields.linkerField
const linkedField = fields.linkedField

if (field.autocolumn) {
Expand All @@ -358,7 +366,7 @@ class LinkController {
const existingSchema = linkedTable.schema[field.fieldName]
if (
existingSchema != null &&
!this.areSchemasEqual(existingSchema, linkedField)
!this.areLinkSchemasEqual(existingSchema, linkedField)
) {
throw new Error("Cannot overwrite existing column.")
}
Expand Down Expand Up @@ -416,6 +424,7 @@ class LinkController {
await this._db.put(linkedTable)
}
} catch (err) {
/* istanbul ignore next */
Sentry.captureException(err)
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/db/linkedRows/linkUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ exports.getLinkDocuments = async function({
await exports.createLinkView(appId)
return exports.getLinkDocuments(arguments[0])
} else {
/* istanbul ignore next */
Sentry.captureException(err)
}
}
Expand Down
218 changes: 218 additions & 0 deletions packages/server/src/db/tests/linkController.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
const TestConfig = require("../../tests/utilities/TestConfiguration")
const { basicRow, basicLinkedRow, basicTable } = require("../../tests/utilities/structures")
const LinkController = require("../linkedRows/LinkController")
const { RelationshipTypes } = require("../../constants")
const { cloneDeep } = require("lodash/fp")

describe("test the link controller", () => {
let config = new TestConfig(false)
let table1, table2

beforeEach(async () => {
await config.init()
const { _id } = await config.createTable()
table2 = await config.createLinkedTable(RelationshipTypes.MANY_TO_MANY, ["link", "link2"])
// update table after creating link
table1 = await config.getTable(_id)
})

afterAll(config.end)

function createLinkController(table, row = null, oldTable = null) {
const linkConfig = {
appId: config.getAppId(),
tableId: table._id,
table,
}
if (row) {
linkConfig.row = row
}
if (oldTable) {
linkConfig.oldTable = oldTable
}
return new LinkController(linkConfig)
}

async function createLinkedRow(linkField = "link", t1 = table1, t2 = table2) {
const row = await config.createRow(basicRow(t2._id))
const { _id } = await config.createRow(basicLinkedRow(t1._id, row._id, linkField))
return config.getRow(t1._id, _id)
}

it("should be able to confirm if two table schemas are equal", () => {
const controller = createLinkController(table1)
let equal = controller.areLinkSchemasEqual(table2.schema.link, table2.schema.link)
expect(equal).toEqual(true)
equal = controller.areLinkSchemasEqual(table1.schema.link, table2.schema.link)
expect(equal).toEqual(false)
})

it("should be able to check the relationship types across two fields", () => {
const controller = createLinkController(table1)
// empty case
let output = controller.handleRelationshipType({}, {})
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY)
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY)
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.MANY_TO_MANY }, {})
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY)
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_MANY)
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.MANY_TO_ONE }, {})
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.ONE_TO_MANY)
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.MANY_TO_ONE)
output = controller.handleRelationshipType({ relationshipType: RelationshipTypes.ONE_TO_MANY }, {})
expect(output.linkedField.relationshipType).toEqual(RelationshipTypes.MANY_TO_ONE)
expect(output.linkerField.relationshipType).toEqual(RelationshipTypes.ONE_TO_MANY)
})

it("should be able to delete a row", async () => {
const row = await createLinkedRow()
const controller = createLinkController(table1, row)
// get initial count
const beforeLinks = await controller.getRowLinkDocs(row._id)
await controller.rowDeleted()
let afterLinks = await controller.getRowLinkDocs(row._id)
expect(beforeLinks.length).toEqual(1)
expect(afterLinks.length).toEqual(0)
})

it("shouldn't throw an error when deleting a row with no links", async () => {
const row = await config.createRow(basicRow(table1._id))
const controller = createLinkController(table1, row)
let error
try {
await controller.rowDeleted()
} catch (err) {
error = err
}
expect(error).toBeUndefined()
})

it("should throw an error when validating a table which is invalid", () => {
const controller = createLinkController(table1)
const copyTable = {
...table1
}
copyTable.schema.otherTableLink = {
type: "link",
fieldName: "link",
tableId: table2._id,
}
let error
try {
controller.validateTable(copyTable)
} catch (err) {
error = err
}
expect(error).toBeDefined()
expect(error.message).toEqual("Cannot re-use the linked column name for a linked table.")
})

it("should be able to remove a link when saving/updating the row", async () => {
const row = await createLinkedRow()
// remove the link from the row
row.link = []
const controller = createLinkController(table1, row)
await controller.rowSaved()
let links = await controller.getRowLinkDocs(row._id)
expect(links.length).toEqual(0)
})

it("should be able to delete a table and have links deleted", async () => {
await createLinkedRow()
const controller = createLinkController(table1)
let before = await controller.getTableLinkDocs()
await controller.tableDeleted()
let after = await controller.getTableLinkDocs()
expect(before.length).toEqual(1)
expect(after.length).toEqual(0)
})

it("should be able to remove a linked field from a table", async () => {
await createLinkedRow()
await createLinkedRow("link2")
const controller = createLinkController(table1, null, table1)
let before = await controller.getTableLinkDocs()
await controller.removeFieldFromTable("link")
let after = await controller.getTableLinkDocs()
expect(before.length).toEqual(2)
// shouldn't delete the other field
expect(after.length).toEqual(1)
})

it("should throw an error when overwriting a link column", async () => {
const update = cloneDeep(table1)
update.schema.link.relationshipType = RelationshipTypes.MANY_TO_ONE
let error
try {
const controller = createLinkController(update)
await controller.tableSaved()
} catch (err) {
error = err
}
expect(error).toBeDefined()
})

it("should be able to remove a field view table update", async () => {
await createLinkedRow()
await createLinkedRow()
const newTable = cloneDeep(table1)
delete newTable.schema.link
const controller = createLinkController(newTable, null, table1)
await controller.tableUpdated()
const links = await controller.getTableLinkDocs()
expect(links.length).toEqual(0)
})

it("shouldn't allow one to many having many relationships against it", async () => {
const firstTable = await config.createTable()
const { _id } = await config.createLinkedTable(RelationshipTypes.MANY_TO_ONE, ["link"])
const linkTable = await config.getTable(_id)
// an initial row to link around
const row = await createLinkedRow("link", linkTable, firstTable)
let error
try {
// create another row to initiate the error
await config.createRow(basicLinkedRow(row.tableId, row.link[0]))
} catch (err) {
error = err
}
expect(error).toBeDefined()
})

it("should not error if a link being created doesn't exist", async () => {
let error
try {
await config.createRow(basicLinkedRow(table1._id, "invalid"))
} catch (err) {
error = err
}
expect(error).toBeUndefined()
})

it("make sure auto column goes onto other row too", async () => {
const table = await config.createTable()
const tableCfg = basicTable()
tableCfg.schema.link = {
type: "link",
fieldName: "link",
tableId: table._id,
name: "link",
autocolumn: true,
}
await config.createTable(tableCfg)
const afterTable = await config.getTable(table._id)
expect(afterTable.schema.link.autocolumn).toBe(true)
})

it("should be able to link to self", async () => {
const table = await config.createTable()
table.schema.link = {
type: "link",
fieldName: "link",
tableId: table._id,
name: "link",
autocolumn: true,
}
await config.updateTable(table)
})
})
Loading