Skip to content

Commit

Permalink
Backend test example
Browse files Browse the repository at this point in the history
  • Loading branch information
rmarscher committed Oct 4, 2023
1 parent 2595bb5 commit 9918e0d
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 10 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ jobs:
- name: 📥 Monorepo install
uses: ./.github/actions/pnpm-install

- name: Run tests
run: pnpm --filter @t4/api test
env:
NO_D1_WARNING: true
CI: true

- name: Migrate database
run: pnpm --filter @t4/api migrate
env:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@
"check-dependency-version-consistency": "^4.1.0",
"cross-env": "^7.0.3",
"eslint": "^8.50.0",
"miniflare": "3.20230922.0",
"node-gyp": "^9.4.0",
"prettier": "^3.0.3",
"react-native-url-polyfill": "^2.0.0",
"turbo": "^1.10.14",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"wrangler": "^3.10.1"
},
"engines": {
"node": ">=18.16.1",
Expand Down
20 changes: 20 additions & 0 deletions packages/api/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export default {
preset: 'ts-jest/presets/default-esm',
transform: {
'^.+\\.ts$': [
'ts-jest',
{
tsconfig: 'test/tsconfig.json',
useESM: true,
},
],
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^(\\.{1,2}/.*)\\.js$': '$1',
},
testEnvironment: 'miniflare',
testEnvironmentOptions: {
modules: true,
},
}
11 changes: 9 additions & 2 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "2.0.0",
"main": "src/index.ts",
"license": "MIT",
"type": "module",
"scripts": {
"dev": "cross-env NO_D1_WARNING=true wrangler dev src/index.ts",
"generate": "drizzle-kit generate:sqlite --schema=./src/db/schema.ts --out=./migrations",
Expand All @@ -13,6 +14,8 @@
"studio": "drizzle-kit studio",
"deploy": "cross-env NO_D1_WARNING=true wrangler deploy --minify src/index.ts",
"postinstall": "pnpm generate",
"test": "jest",
"types": "cross-env NO_D1_WARNING=true wrangler types",
"with-env": "dotenv -e ../../.env.local --",
"clean": "rm -rf .turbo node_modules"
},
Expand All @@ -28,11 +31,15 @@
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20230922.0",
"@types/jest": "^29.5.5",
"better-sqlite3": "^8.6.0",
"dotenv-cli": "^7.3.0",
"drizzle-kit": "^0.19.13",
"eslint": "^8.50.0",
"typescript": "^5.2.2",
"wrangler": "3.6.0"
"jest": "^29.7.0",
"jest-environment-miniflare": "^2.14.1",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
}
}
6 changes: 3 additions & 3 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { cors } from 'hono/cors'
import { createContext } from './context'
import { trpcServer } from '@hono/trpc-server'

type Bindings = {
DB: D1Database
export type Bindings = Env & {
JWT_VERIFICATION_KEY: string
APP_URL: string
[k: string]: unknown
}

const app = new Hono<{ Bindings: Bindings }>()
Expand All @@ -28,7 +28,7 @@ app.use('/trpc/*', async (c, next) => {
return await trpcServer({
router: appRouter,
createContext: async (opts) => {
return await createContext(c.env.DB, c.env.JWT_VERIFICATION_KEY, opts)
return await createContext(c.env.__D1_BETA__DB, c.env.JWT_VERIFICATION_KEY, opts)
},
})(c, next)
})
Expand Down
8 changes: 8 additions & 0 deletions packages/api/test/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Bindings } from '../src'

declare global {
function getMiniflareBindings(): Bindings
function getMiniflareDurableObjectStorage(id: DurableObjectId): Promise<DurableObjectStorage>
}

export {}
66 changes: 66 additions & 0 deletions packages/api/test/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client'
import jwt from '@tsndr/cloudflare-worker-jwt'
import * as dotenv from 'dotenv'
import { readMigrationFiles } from 'drizzle-orm/migrator'
import superjson from 'superjson'
import app, { Bindings } from '../src'
import { createDb } from '../src/db/client'
import { AppRouter } from '../src/router'
dotenv.config({ path: './.dev.vars' })

const bindings = getMiniflareBindings()

export const env: Bindings = {
...bindings,
// allow tests to work without env vars
APP_URL: bindings.APP_URL || process.env.APP_URL || 'http://localhost:3000',
JWT_VERIFICATION_KEY:
bindings.JWT_VERIFICATION_KEY || process.env.JWT_VERIFICATION_KEY || '12345',
}
export const db = createDb(env.__D1_BETA__DB)

export async function executeSql(sql: string) {
return await env.__D1_BETA__DB.exec(sql)
}

export async function migrateDb() {
const files = readMigrationFiles({ migrationsFolder: './migrations' })
for (let i = 0; i < files.length; i++) {
const migrationMeta = files[i]
for (let j = 0; j < migrationMeta.sql.length; j++) {
await executeSql(migrationMeta.sql[j].replaceAll(/[\n\t]/g, ''))
}
}
}

export async function createSessionToken({ userId = 'test-user' }: { userId?: string }) {
const payload = {
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 7,
sub: userId,
}
const algorithm = 'HS256'
return await jwt.sign(payload, env.JWT_VERIFICATION_KEY, { algorithm })
}

export function createTRPCClient({
userId,
url = 'http://localhost:3000/trpc',
}: {
userId?: string
url?: string
}) {
return createTRPCProxyClient<AppRouter>({
transformer: superjson,
links: [
httpBatchLink({
url,
async headers() {
return {
authorization: userId ? 'Bearer ' + (await createSessionToken({ userId })) : undefined,
}
},
fetch: async (resource, options) => app.request(resource, options, env),
}),
],
})
}
10 changes: 10 additions & 0 deletions packages/api/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { env } from './helpers'

describe('T4 App API Server', () => {
test('has cloudflare bindings', async () => {
expect(env.__D1_BETA__DB).toBeTruthy()
})
test('has env vars', async () => {
expect(env.JWT_VERIFICATION_KEY).toBeTruthy()
})
})
13 changes: 13 additions & 0 deletions packages/api/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"types": [
"@cloudflare/workers-types",
"node",
"jest",
"./globals.d.ts",
"../worker-configuration.d.ts"
]
},
"include": ["../src/**/*", "**/*"]
}
19 changes: 19 additions & 0 deletions packages/api/test/user.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { UserTable } from '../src/db/schema'
import { createSessionToken, createTRPCClient, db, migrateDb } from './helpers'

// Note: if your routers make any external requests, you can mock them with getMiniflareFetchMock()
// https://github.com/cloudflare/miniflare/blob/master/docs/src/content/testing/jest.md#mocking-outbound-fetch-requests

describe('User router', () => {
beforeAll(async () => {
await migrateDb()
})

test('current', async () => {
const userId = 'test-user'
await db.insert(UserTable).values({ id: userId, email: '[email protected]' })
const client = createTRPCClient({ userId })
const res = await client.user.current.query()
expect(res?.id).toBe(userId)
})
})
2 changes: 1 addition & 1 deletion packages/api/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"skipLibCheck": true,
"strict": true,
"lib": ["esnext"],
"types": ["@cloudflare/workers-types", "node"],
"types": ["@cloudflare/workers-types/2023-07-01", "node", "./worker-configuration.d.ts"],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
Expand Down
4 changes: 4 additions & 0 deletions packages/api/worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Generated by Wrangler on Tue Oct 03 2023 20:10:16 GMT-0400 (Eastern Daylight Time)
interface Env {
__D1_BETA__DB: D1Database;
}
4 changes: 2 additions & 2 deletions packages/api/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ name = "t4-api"
compatibility_date = "2023-09-22"
send_metrics = false
node_compat = true
main = "src/worker.ts"
main = "src/index.ts"
account_id = "719e3374c9fabba87860b809acb3c939"

[[d1_databases]]
binding = "DB"
binding = "__D1_BETA__DB"
database_name = "production"
database_id = "aa5e3923-0f1b-469a-8bb8-d10fb866efe0"
migrations_dir = "migrations"
Expand Down

0 comments on commit 9918e0d

Please sign in to comment.