Skip to content

Commit

Permalink
chore(code-gen/experimental): add e2e crud tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dirkdev98 committed Apr 27, 2023
1 parent f789598 commit 64d07ce
Show file tree
Hide file tree
Showing 27 changed files with 567 additions and 1,283 deletions.
2 changes: 1 addition & 1 deletion examples/crud/.env → examples/crud-basic/.env
@@ -1,5 +1,5 @@
NODE_ENV=development
APP_NAME=crud
APP_NAME=crud-basic

POSTGRES_HOST=127.0.0.1:5432
POSTGRES_USER=postgres
Expand Down
12 changes: 5 additions & 7 deletions examples/crud/README.md → examples/crud-basic/README.md
@@ -1,19 +1,17 @@
# CRUD API
# CRUD basics

This project is created using the
[crud](https://github.com/compasjs/compas/tree/main/examples/crud) template via
[create-compas](https://www.npmjs.com/package/create-compas).
[crud-basic](https://github.com/compasjs/compas/tree/main/examples/crud-basic)
template via [create-compas](https://www.npmjs.com/package/create-compas).

```shell
# Via NPM
npx create-compas@latest --template crud
npx create-compas@latest --template crud-basic

# Or with Yarn
yarn create compas --template crud
yarn create compas --template crud-basic
```

This example includes acts as a showcase for various CRUD features.

## Getting started

- Start up the development Postgres and Minio instances
Expand Down
13 changes: 13 additions & 0 deletions examples/crud-basic/migrations/001-post.sql
@@ -0,0 +1,13 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE "post"
(
"id" uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
"contents" varchar NOT NULL,
"title" varchar NOT NULL,
"createdAt" timestamptz NOT NULL DEFAULT now(),
"updatedAt" timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX "postDatesIdx" ON "post" ("createdAt", "updatedAt");
CREATE INDEX "postTitleIdx" ON "post" ("title");
Expand Up @@ -18,7 +18,6 @@
"testing": [
"compas migrate",
"compas test --serial"
],
"initMessage": "Started a new project with the 'crud' template. See the README.md for how to get started."
]
}
}
File renamed without changes.
Expand Up @@ -6,26 +6,29 @@ mainFn(import.meta, main);

function main() {
const generator = new Generator();
const T = new TypeCreator("todo");
const T = new TypeCreator();
const Tdatabase = new TypeCreator("database");

generator.add(
new TypeCreator("database")
.object("todo")
Tdatabase.object("post")
.keys({
title: T.string().min(3).searchable(),
completedAt: T.date().optional().searchable(),
contents: T.string(),
})
.enableQueries({
withDates: true,
}),

T.crud("/todo").entity(T.reference("database", "todo")).routes({
listRoute: true,
singleRoute: true,
createRoute: true,
updateRoute: true,
deleteRoute: true,
}),
new TypeCreator("post")
.crud("/post")
.entity(T.reference("database", "post"))
.routes({
listRoute: true,
singleRoute: true,
createRoute: true,
updateRoute: true,
deleteRoute: true,
}),
);

generator.generate({
Expand All @@ -35,6 +38,7 @@ function main() {
database: {
target: {
dialect: "postgres",
includeDDL: true,
},
},
apiClient: {
Expand All @@ -46,6 +50,9 @@ function main() {
looseObjectValidation: false,
},
},
types: {
declareGlobalTypes: true,
},
router: {
target: {
library: "koa",
Expand Down
Expand Up @@ -4,7 +4,7 @@ import {
newPostgresConnection,
} from "@compas/store";
import { router } from "./generated/common/router.js";
import { todoRegisterCrud } from "./generated/todo/crud.js";
import { postRegisterCrud } from "./generated/post/crud.js";

export let app = undefined;

Expand All @@ -14,9 +14,7 @@ export async function injectServices() {
app = getApp({});
sql = await newPostgresConnection({ max: 10 });

await todoRegisterCrud({
sql,
});
injectCrud();

app.use(router(createBodyParsers()));
}
Expand All @@ -25,9 +23,14 @@ export async function injectTestServices() {
app = getApp({});
sql = await createTestPostgresDatabase();

await todoRegisterCrud({
sql,
});
injectCrud();

app.use(router(createBodyParsers()));
}

/**
* Register all crud routes
*/
function injectCrud() {
postRegisterCrud({ sql });
}
239 changes: 239 additions & 0 deletions examples/crud-basic/test/e2e.test.js
@@ -0,0 +1,239 @@
import { mainTestFn, test } from "@compas/cli";
import { uuid } from "@compas/stdlib";
import { cleanupTestPostgresDatabase } from "@compas/store";
import {
fetchCatchErrorAndWrapWithAppError,
fetchWithBaseUrl,
} from "../src/generated/common/api-client.js";
import { queries } from "../src/generated/common/database.js";
import { queryPost } from "../src/generated/database/post.js";
import {
apiPostCreate,
apiPostDelete,
apiPostList,
apiPostSingle,
apiPostUpdate,
} from "../src/generated/post/apiClient.js";
import { app, injectTestServices, sql } from "../src/services.js";

mainTestFn(import.meta);

test("e2e", async (t) => {
const apiPort = 5500;

await injectTestServices();

const server = await new Promise((r) => {
const server = app.listen(apiPort, () => {
r(server);
});
});

const fetchFn = fetchCatchErrorAndWrapWithAppError(
fetchWithBaseUrl(fetch, `http://localhost:${apiPort}/`),
);

await queries.postInsert(
sql,
Array.from({ length: 10 }).map((_, idx) => ({
title: `Title #${String(idx).padStart(2, "0")}`,
contents: `Post contents for post #${idx}`,
})),
);

t.test("apiPostList", (t) => {
t.test("success", async (t) => {
const result = await apiPostList(fetchFn, {}, {});

t.equal(result.total, 10);
t.equal(result.list.length, 10);
});

t.test("offset + limit", async (t) => {
const result = await apiPostList(
fetchFn,
{
offset: 1,
limit: 1,
},
{},
);

t.equal(result.total, 10);
t.equal(result.list.length, 1);
});

t.test("orderBy", async (t) => {
const result = await apiPostList(
fetchFn,
{},
{
orderBy: ["title"],
orderBySpec: {
title: "DESC",
},
},
);

t.equal(result.list[0].title, `Title #09`);
t.equal(result.list.at(-1).title, `Title #00`);
});

t.test("where", (t) => {
t.test("contents - not allowed", async (t) => {
try {
await apiPostList(
fetchFn,
{},
{
where: {
contents: "fail",
},
},
);
} catch (e) {
t.equal(e.key, "validator.error");
}
});

t.test("title - exact match", async (t) => {
const result = await apiPostList(
fetchFn,
{},
{
where: {
title: "Title #00",
},
},
);

t.equal(result.total, 1);
t.equal(result.list[0].title, "Title #00");
});
});
});

t.test("apiPostSingle", (t) => {
t.test("post.single.notFound", async (t) => {
try {
await apiPostSingle(fetchFn, {
postId: uuid(),
});
} catch (e) {
t.equal(e.key, "post.single.notFound");
}
});

t.test("success", async (t) => {
const [post] = await queryPost({ limit: 1 }).exec(sql);

const result = await apiPostSingle(fetchFn, {
postId: post.id,
});

t.equal(result.item.id, post.id);
});
});

t.test("apiPostCreate", (t) => {
t.test("success", async (t) => {
const result = await apiPostCreate(fetchFn, {
title: "Title",
contents: "Contents",
});

const { item } = await apiPostSingle(fetchFn, {
postId: result.item.id,
});

t.equal(item.title, "Title");
t.equal(item.contents, "Contents");
});
});

t.test("apiPostUpdate", (t) => {
t.test("post.single.notFound", async (t) => {
try {
await apiPostUpdate(
fetchFn,
{
postId: uuid(),
},
{
title: "Updated",
contents: "Updated",
},
);
} catch (e) {
t.equal(e.key, "post.single.notFound");
}
});

t.test("success", async (t) => {
const [post] = await queryPost({ limit: 1 }).exec(sql);

await apiPostUpdate(
fetchFn,
{
postId: post.id,
},
{
title: "Updated",
contents: "Updated",
},
);

const { item } = await apiPostSingle(fetchFn, { postId: post.id });

t.equal(item.title, "Updated");
t.equal(item.contents, "Updated");
});

t.test("only updates one post", async (t) => {
const result = await apiPostList(
fetchFn,
{},
{
where: {
title: "Updated",
},
},
);

t.equal(result.total, 1);
});
});

t.test("apiPostDelete", (t) => {
t.test("post.single.notFound", async (t) => {
try {
await apiPostDelete(fetchFn, {
postId: uuid(),
});
} catch (e) {
t.equal(e.key, "post.single.notFound");
}
});

t.test("success", async (t) => {
const [post] = await queryPost({ limit: 1 }).exec(sql);

await apiPostDelete(fetchFn, { postId: post.id });

try {
await apiPostSingle(fetchFn, {
postId: post.id,
});
} catch (e) {
t.equal(e.key, "post.single.notFound");
}
});
});

t.test("teardown", async (t) => {
server.close();
await cleanupTestPostgresDatabase(sql);

t.pass();
});
});
@@ -1,5 +1,5 @@
NODE_ENV=development
APP_NAME=crud-simple-todo
APP_NAME=crud-inline

POSTGRES_HOST=127.0.0.1:5432
POSTGRES_USER=postgres
Expand Down

0 comments on commit 64d07ce

Please sign in to comment.