Skip to content

Commit

Permalink
add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MeowningMaster committed May 16, 2024
1 parent b8cacce commit a9025da
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 24 deletions.
28 changes: 28 additions & 0 deletions examples/tests/src/emit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { beforeAll, expect, mock, test } from "bun:test"
import { faker } from "@faker-js/faker"
import { Type } from "@sinclair/typebox"
import { Client } from "@wsx/client"
import { Wsx } from "@wsx/server"

const onEmit = mock((message: string) => void message)

async function initialize() {
const wsx = new Wsx()
.route("/some", ({ body }) => onEmit(body), { body: Type.String() })
.listen(0)
const { port } = wsx.server!
return await Client<typeof wsx>(`ws://localhost:${port}`)
}

let routes: Awaited<ReturnType<typeof initialize>>["routes"]
beforeAll(async () => {
const client = await initialize()
routes = client.routes
})

test("valid", async () => {
const message = faker.lorem.sentence()
routes.some.emit(message)
await Bun.sleep(10)
expect(onEmit).toBeCalledWith(message)
})
29 changes: 29 additions & 0 deletions examples/tests/src/rpc.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { beforeAll, expect, mock, test } from "bun:test"
import { faker } from "@faker-js/faker"
import { Type } from "@sinclair/typebox"
import { Client } from "@wsx/client"
import { Wsx } from "@wsx/server"

const onRpc = mock((message: string) => message)

async function initialize() {
const wsx = new Wsx()
.route("/some", ({ body }) => onRpc(body), { body: Type.String() })
.listen(0)
const { port } = wsx.server!
return await Client<typeof wsx>(`ws://localhost:${port}`)
}

let routes: Awaited<ReturnType<typeof initialize>>["routes"]
beforeAll(async () => {
const client = await initialize()
routes = client.routes
})

test("valid", async () => {
const message = faker.lorem.sentence()
const response = await routes.some.call(message)
expect(response.status).toBe("success")
expect(response.body).toBe(message)
expect(onRpc).toBeCalledWith(message)
})
46 changes: 32 additions & 14 deletions examples/tests/src/validation.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
import { expect, test } from "bun:test"
import { beforeAll, expect, test } from "bun:test"
import { faker } from "@faker-js/faker"
import { Type } from "@sinclair/typebox"
import { type Static, Type } from "@sinclair/typebox"
import { Client } from "@wsx/client"
import { Wsx } from "@wsx/server"

const wsx = new Wsx()
.route("/valid", ({ body }) => body, {
body: Type.Object({ message: Type.String() }),
response: Type.Object({ message: Type.String() }),
})
.listen(0)
type ValidBody = Static<typeof ValidBody>
const ValidBody = Type.Object({ message: Type.String() })

async function initialize() {
const wsx = new Wsx()
.route("/validate", ({ body }) => body, {
body: ValidBody,
response: Type.Object({ message: Type.String() }),
})
.listen(0)

const { port } = wsx.server!
const { port } = wsx.server!

const { routes } = await Client<typeof wsx>(`ws://localhost:${port}`)
return await Client<typeof wsx>(`ws://localhost:${port}`)
}

let routes: Awaited<ReturnType<typeof initialize>>["routes"]
beforeAll(async () => {
const client = await initialize()
routes = client.routes
})

test("valid", async () => {
const message = faker.lorem.sentence()
const response = await routes.valid.call({
const response = await routes.validate.call({
message,
})
console.log(response)
expect(response.data).toBeObject()
expect(response.data!.message).toBe(message)
expect(response.status).toBe("success")
expect((response.body as ValidBody).message).toBe(message)
})

test("invalid", async () => {
const response = await routes.validate.call({
message: 5 as any,
})
expect(response.status).toBe("fail")
expect((response as { message: string }).message).toBe("Validation failed")
})
24 changes: 20 additions & 4 deletions libs/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ import type { Wsx } from "@wsx/server"
import type { ClientConfig, ClientType } from "./types"
export type { ClientType, ClientConfig, ClientWs } from "./types"

import { Proto, type RPCHandler, isPromise, subprotocol } from "@wsx/shared"
import {
Proto,
type RPCHandler,
type RpcResponse,
isPromise,
subprotocol,
} from "@wsx/shared"

type Method = (typeof methods)[number]
const methods = ["call", "emit", "listen", "unlisten"] as const

const locals = ["localhost", "127.0.0.1", "0.0.0.0"]

type Resolve = (response: unknown) => void
type Resolve = (response: RpcResponse) => void

class Store {
/**
Expand Down Expand Up @@ -108,13 +114,23 @@ export const Client = <
const action = JSON.parse(data) as Proto.GenericAction
const [actionType] = action
if (Proto.isRpcResponse(action)) {
const [, id, body] = action
const [, id] = action
const resolve = store.resolvers.get(id)
if (!resolve) {
console.error("No resolver for call", id)
return
}
resolve(body)

if (actionType === Proto.actionTypes.rpc.response.success) {
const [, , body] = action
resolve({ status: "success", body })
} else if (actionType === Proto.actionTypes.rpc.response.fail) {
const [, , message, body] = action
resolve({ status: "fail", body, message })
} else if (actionType === Proto.actionTypes.rpc.response.error) {
const [, , message, body, code] = action
resolve({ status: "error", body, message, code })
}
return
}

Expand Down
4 changes: 2 additions & 2 deletions libs/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class WsxHandler implements WebSocketHandler {

const route = this.wsx.router.get(path)
if (!route) {
console.debug("Route not found", { path })
// console.debug("Route not found", { path })
if (isRpcRequest) {
ws[sendSymbol]([
Proto.actionTypes.rpc.response.fail,
Expand All @@ -96,7 +96,7 @@ export class WsxHandler implements WebSocketHandler {
if (bodySchema) {
const validationResult = await validate(bodySchema, body)
if (!validationResult.success) {
console.debug("Validation failed", validationResult.issues)
// console.debug("Validation failed", validationResult.issues)
if (isRpcRequest) {
ws[sendSymbol]([
Proto.actionTypes.rpc.response.fail,
Expand Down
15 changes: 11 additions & 4 deletions libs/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,19 @@ export type RPCOptions = {

export type RpcResponse<Response = unknown> =
| {
data: Response
error?: undefined
status: "success"
body: Response
}
| {
data?: undefined
error: unknown
status: "fail"
message: string
body: unknown
}
| {
status: "error"
message: string
code?: number
body?: unknown
}

export function isObject(x: unknown): x is object {
Expand Down

0 comments on commit a9025da

Please sign in to comment.