diff --git a/packages/server/package.json b/packages/server/package.json
index 3fe0d68bf3..99763109f3 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -33,7 +33,7 @@
},
"scripts": {
"test": "jest --testPathIgnorePatterns=routes && npm run test:integration",
- "test:integration": "jest routes --runInBand --coverage",
+ "test:integration": "jest --runInBand --coverage",
"test:watch": "jest --watch",
"run:docker": "node src/index",
"dev:builder": "cross-env PORT=4001 nodemon src/index.js",
diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js
index 1cd69c54df..96754f17cc 100644
--- a/packages/server/src/api/controllers/apikeys.js
+++ b/packages/server/src/api/controllers/apikeys.js
@@ -3,20 +3,13 @@ const { join } = require("../../utilities/centralPath")
const readline = require("readline")
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
const env = require("../../environment")
-const selfhost = require("../../selfhost")
const ENV_FILE_PATH = "/.env"
exports.fetch = async function(ctx) {
ctx.status = 200
- if (env.SELF_HOSTED) {
- ctx.body = {
- selfhost: await selfhost.getSelfHostAPIKey(),
- }
- } else {
- ctx.body = {
- budibase: env.BUDIBASE_API_KEY,
- userId: env.USERID_API_KEY,
- }
+ ctx.body = {
+ budibase: env.BUDIBASE_API_KEY,
+ userId: env.USERID_API_KEY,
}
}
diff --git a/packages/server/src/api/controllers/row.js b/packages/server/src/api/controllers/row.js
index 5d783b7d18..bf985fe55d 100644
--- a/packages/server/src/api/controllers/row.js
+++ b/packages/server/src/api/controllers/row.js
@@ -224,6 +224,7 @@ exports.fetchView = async function(ctx) {
try {
table = await db.get(viewInfo.meta.tableId)
} catch (err) {
+ /* istanbul ignore next */
table = {
schema: {},
}
@@ -255,16 +256,24 @@ exports.fetchView = async function(ctx) {
exports.search = async function(ctx) {
const appId = ctx.user.appId
-
const db = new CouchDB(appId)
-
const {
query,
pagination: { pageSize = 10, page },
} = ctx.request.body
- query.tableId = ctx.params.tableId
+ // make all strings a starts with operation rather than pure equality
+ for (const [key, queryVal] of Object.entries(query)) {
+ if (typeof queryVal === "string") {
+ query[key] = {
+ $gt: queryVal,
+ $lt: `${queryVal}\uffff`,
+ }
+ }
+ }
+ // pure equality for table
+ query.tableId = ctx.params.tableId
const response = await db.find({
selector: query,
limit: pageSize,
@@ -324,7 +333,6 @@ exports.destroy = async function(ctx) {
const row = await db.get(ctx.params.rowId)
if (row.tableId !== ctx.params.tableId) {
ctx.throw(400, "Supplied tableId doesn't match the row's tableId")
- return
}
await linkRows.updateLinks({
appId,
@@ -376,15 +384,6 @@ exports.fetchEnrichedRow = async function(ctx) {
const db = new CouchDB(appId)
const tableId = ctx.params.tableId
const rowId = ctx.params.rowId
- if (appId == null || tableId == null || rowId == null) {
- ctx.status = 400
- ctx.body = {
- status: 400,
- error:
- "Cannot handle request, URI params have not been successfully prepared.",
- }
- return
- }
// need table to work out where links go in row
let [table, row] = await Promise.all([
db.get(tableId),
diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js
index 7b063cb522..7628fa2077 100644
--- a/packages/server/src/api/index.js
+++ b/packages/server/src/api/index.js
@@ -41,13 +41,15 @@ router.use(async (ctx, next) => {
try {
await next()
} catch (err) {
- ctx.log.error(err)
ctx.status = err.status || err.statusCode || 500
ctx.body = {
message: err.message,
status: ctx.status,
}
- console.trace(err)
+ if (env.NODE_ENV !== "jest") {
+ ctx.log.error(err)
+ console.trace(err)
+ }
}
})
diff --git a/packages/server/src/api/routes/static.js b/packages/server/src/api/routes/static.js
index 2b0c9b36fc..c812c4d3b1 100644
--- a/packages/server/src/api/routes/static.js
+++ b/packages/server/src/api/routes/static.js
@@ -8,6 +8,7 @@ const usage = require("../../middleware/usageQuota")
const router = Router()
+/* istanbul ignore next */
router.param("file", async (file, ctx, next) => {
ctx.file = file && file.includes(".") ? file : "index.html"
diff --git a/packages/server/src/api/routes/tests/apikeys.spec.js b/packages/server/src/api/routes/tests/apikeys.spec.js
index a8077a4492..dbee57c8b0 100644
--- a/packages/server/src/api/routes/tests/apikeys.spec.js
+++ b/packages/server/src/api/routes/tests/apikeys.spec.js
@@ -35,7 +35,7 @@ describe("/api/keys", () => {
describe("update", () => {
it("should allow updating a value", async () => {
- fs.writeFileSync(path.join(budibaseAppsDir(), ".env"), "")
+ fs.writeFileSync(path.join(budibaseAppsDir(), ".env"), "TEST_API_KEY=thing")
const res = await request
.put(`/api/keys/TEST`)
.send({
diff --git a/packages/server/src/api/routes/tests/cloud.spec.js b/packages/server/src/api/routes/tests/cloud.spec.js
new file mode 100644
index 0000000000..3cb65ed819
--- /dev/null
+++ b/packages/server/src/api/routes/tests/cloud.spec.js
@@ -0,0 +1,16 @@
+const setup = require("./utilities")
+
+describe("test things in the Cloud/Self hosted", () => {
+ describe("test self hosted static page", () => {
+ it("should be able to load the static page", async () => {
+ await setup.switchToCloudForFunction(async () => {
+ let request = setup.getRequest()
+ let config = setup.getConfig()
+ await config.init()
+ const res = await request.get(`/`).expect(200)
+ expect(res.text.includes("
Budibase self hosting️")).toEqual(true)
+ setup.afterAll()
+ })
+ })
+ })
+})
diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js
index cc50520b77..1442e4eb75 100644
--- a/packages/server/src/api/routes/tests/row.spec.js
+++ b/packages/server/src/api/routes/tests/row.spec.js
@@ -17,15 +17,15 @@ describe("/rows", () => {
row = basicRow(table._id)
})
- const loadRow = async id =>
+ const loadRow = async (id, status = 200) =>
await request
.get(`/api/${table._id}/rows/${id}`)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
- .expect(200)
+ .expect(status)
- describe("save, load, update, delete", () => {
+ describe("save, load, update", () => {
it("returns a success message when the row is created", async () => {
const res = await request
.post(`/api/${row.tableId}/rows`)
@@ -217,38 +217,152 @@ describe("/rows", () => {
expect(savedRow.body.description).toEqual(existing.description)
expect(savedRow.body.name).toEqual("Updated Name")
-
+ })
+
+ it("should throw an error when given improper types", async () => {
+ const existing = await config.createRow()
+ await request
+ .patch(`/api/${table._id}/rows/${existing._id}`)
+ .send({
+ _id: existing._id,
+ _rev: existing._rev,
+ tableId: table._id,
+ name: 1,
+ })
+ .set(config.defaultHeaders())
+ .expect(400)
+ })
+ })
+
+ describe("destroy", () => {
+ it("should be able to delete a row", async () => {
+ const createdRow = await config.createRow(row)
+ const res = await request
+ .delete(`/api/${table._id}/rows/${createdRow._id}/${createdRow._rev}`)
+ .set(config.defaultHeaders())
+ .expect('Content-Type', /json/)
+ .expect(200)
+ expect(res.body.ok).toEqual(true)
+ })
+
+ it("shouldn't allow deleting a row in a table which is different to the one the row was created on", async () => {
+ const createdRow = await config.createRow(row)
+ await request
+ .delete(`/api/wrong_table/rows/${createdRow._id}/${createdRow._rev}`)
+ .set(config.defaultHeaders())
+ .expect(400)
})
})
describe("validate", () => {
it("should return no errors on valid row", async () => {
- const result = await request
+ const res = await request
.post(`/api/${table._id}/rows/validate`)
.send({ name: "ivan" })
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
- expect(result.body.valid).toBe(true)
- expect(Object.keys(result.body.errors)).toEqual([])
+ expect(res.body.valid).toBe(true)
+ expect(Object.keys(res.body.errors)).toEqual([])
})
it("should errors on invalid row", async () => {
- const result = await request
+ const res = await request
.post(`/api/${table._id}/rows/validate`)
.send({ name: 1 })
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
- expect(result.body.valid).toBe(false)
- expect(Object.keys(result.body.errors)).toEqual(["name"])
+ expect(res.body.valid).toBe(false)
+ expect(Object.keys(res.body.errors)).toEqual(["name"])
})
})
- describe("enrich row unit test", () => {
+ describe("bulkDelete", () => {
+ it("should be able to delete a bulk set of rows", async () => {
+ const row1 = await config.createRow()
+ const row2 = await config.createRow()
+ const res = await request
+ .post(`/api/${table._id}/rows`)
+ .send({
+ type: "delete",
+ rows: [
+ row1,
+ row2,
+ ]
+ })
+ .set(config.defaultHeaders())
+ .expect('Content-Type', /json/)
+ .expect(200)
+ expect(res.body.length).toEqual(2)
+ await loadRow(row1._id, 404)
+ })
+ })
+
+ describe("search", () => {
+ it("should run a search on the table", async () => {
+ const row = await config.createRow()
+ // add another row that shouldn't be found
+ await config.createRow({
+ ...basicRow(),
+ name: "Other Contact",
+ })
+ const res = await request
+ .post(`/api/${table._id}/rows/search`)
+ .send({
+ query: {
+ name: "Test",
+ },
+ pagination: { pageSize: 25, page: 0 }
+ })
+ .set(config.defaultHeaders())
+ .expect('Content-Type', /json/)
+ .expect(200)
+ expect(res.body.length).toEqual(1)
+ expect(res.body[0]._id).toEqual(row._id)
+ })
+ })
+
+ describe("fetchView", () => {
+ it("should be able to fetch tables contents via 'view'", async () => {
+ const row = await config.createRow()
+ const res = await request
+ .get(`/api/views/all_${table._id}`)
+ .set(config.defaultHeaders())
+ .expect('Content-Type', /json/)
+ .expect(200)
+ expect(res.body.length).toEqual(1)
+ expect(res.body[0]._id).toEqual(row._id)
+ })
+
+ it("should throw an error if view doesn't exist", async () => {
+ await request
+ .get(`/api/views/derp`)
+ .set(config.defaultHeaders())
+ .expect(400)
+ })
+
+ it("should be able to run on a view", async () => {
+ const view = await config.createView()
+ const row = await config.createRow()
+ const res = await request
+ .get(`/api/views/${view._id}`)
+ .set(config.defaultHeaders())
+ .expect('Content-Type', /json/)
+ .expect(200)
+ expect(res.body.length).toEqual(1)
+ expect(res.body[0]._id).toEqual(row._id)
+ })
+ })
+
+ describe("user testing", () => {
+
+ })
+
+ describe("fetchEnrichedRows", () => {
it("should allow enriching some linked rows", async () => {
const table = await config.createLinkedTable()
const firstRow = await config.createRow({
@@ -262,30 +376,45 @@ describe("/rows", () => {
link: [{_id: firstRow._id}],
tableId: table._id,
})
- const enriched = await outputProcessing(config.getAppId(), table, [secondRow])
- expect(enriched[0].link.length).toBe(1)
- expect(enriched[0].link[0]._id).toBe(firstRow._id)
- expect(enriched[0].link[0].primaryDisplay).toBe("Test Contact")
+
+ // test basic enrichment
+ const resBasic = await request
+ .get(`/api/${table._id}/rows/${secondRow._id}`)
+ .set(config.defaultHeaders())
+ .expect('Content-Type', /json/)
+ .expect(200)
+ expect(resBasic.body.link[0]._id).toBe(firstRow._id)
+ expect(resBasic.body.link[0].primaryDisplay).toBe("Test Contact")
+
+ // test full enrichment
+ const resEnriched = await request
+ .get(`/api/${table._id}/${secondRow._id}/enrich`)
+ .set(config.defaultHeaders())
+ .expect('Content-Type', /json/)
+ .expect(200)
+ expect(resEnriched.body.link.length).toBe(1)
+ expect(resEnriched.body.link[0]._id).toBe(firstRow._id)
+ expect(resEnriched.body.link[0].name).toBe("Test Contact")
+ expect(resEnriched.body.link[0].description).toBe("original description")
})
})
- it("should allow enriching attachment rows", async () => {
- const table = await config.createAttachmentTable()
- const row = await config.createRow({
- name: "test",
- description: "test",
- attachment: [{
- url: "/test/thing",
- }],
- tableId: table._id,
+ describe("attachments", () => {
+ it("should allow enriching attachment rows", async () => {
+ const table = await config.createAttachmentTable()
+ const row = await config.createRow({
+ name: "test",
+ description: "test",
+ attachment: [{
+ url: "/test/thing",
+ }],
+ tableId: table._id,
+ })
+ // the environment needs configured for this
+ await setup.switchToCloudForFunction(async () => {
+ const enriched = await outputProcessing(config.getAppId(), table, [row])
+ expect(enriched[0].attachment[0].url).toBe(`/app-assets/assets/${config.getAppId()}/test/thing`)
+ })
})
- // the environment needs configured for this
- env.CLOUD = 1
- env.SELF_HOSTED = 1
- const enriched = await outputProcessing(config.getAppId(), table, [row])
- expect(enriched[0].attachment[0].url).toBe(`/app-assets/assets/${config.getAppId()}/test/thing`)
- // remove env config
- env.CLOUD = undefined
- env.SELF_HOSTED = undefined
})
})
\ No newline at end of file
diff --git a/packages/server/src/app.js b/packages/server/src/app.js
index 3779890c9d..15e996cfe6 100644
--- a/packages/server/src/app.js
+++ b/packages/server/src/app.js
@@ -56,7 +56,11 @@ if (electron.app && electron.app.isPackaged) {
const server = http.createServer(app.callback())
destroyable(server)
-server.on("close", () => console.log("Server Closed"))
+server.on("close", () => {
+ if (env.NODE_ENV !== "jest") {
+ console.log("Server Closed")
+ }
+})
module.exports = server.listen(env.PORT || 0, async () => {
console.log(`Budibase running on ${JSON.stringify(server.address())}`)
diff --git a/packages/server/src/middleware/tests/authenticated.spec.js b/packages/server/src/middleware/tests/authenticated.spec.js
index bb124d2f4a..fe7e592528 100644
--- a/packages/server/src/middleware/tests/authenticated.spec.js
+++ b/packages/server/src/middleware/tests/authenticated.spec.js
@@ -9,7 +9,6 @@ class TestConfiguration {
this.ctx = {
config: {},
auth: {},
- request: {},
cookies: {
set: jest.fn(),
get: jest.fn()