Unified · Split
| @@ -3,13 +3,14 @@ import { convexTest } from "convex-test"; · 2 unmodified lines | |||
| 3 | import { api } from "./_generated/api"; | 3 | import { api } from "./_generated/api"; |
| 4 | import type { Id } from "./_generated/dataModel.d.ts"; | 4 | import type { Id } from "./_generated/dataModel.d.ts"; |
| 5 | import schema from "./schema"; | 5 | import schema from "./schema"; |
| 6 | import { registerRateLimiterComponent } from "./testHelpers"; | ||
| 6 | 7 | ||
| 7 | // --------------------------------------------------------------------------- | 8 | // --------------------------------------------------------------------------- |
| 8 | // Helpers | 9 | // Helpers |
| 9 | // --------------------------------------------------------------------------- | 10 | // --------------------------------------------------------------------------- |
| 10 | 11 | ||
| 11 | function setup() { | 12 | function setup() { |
| 12 | return convexTest(schema, { | 13 | const t = convexTest(schema, { |
| 13 | "./workspace.ts": () => import("./workspace"), | 14 | "./workspace.ts": () => import("./workspace"), |
| 14 | "./workspaces.ts": () => import("./workspaces"), | 15 | "./workspaces.ts": () => import("./workspaces"), |
| 15 | "./organizations.ts": () => import("./organizations"), | 16 | "./organizations.ts": () => import("./organizations"), |
| @@ -25,6 +26,9 @@ function setup() { · 9 unmodified lines | |||
| 25 | "./toolRegistry.ts": () => import("./toolRegistry"), | 26 | "./toolRegistry.ts": () => import("./toolRegistry"), |
| 26 | "./_generated/api.js": () => import("./_generated/api.js"), | 27 | "./_generated/api.js": () => import("./_generated/api.js"), |
| 27 | }); | 28 | }); |
| 29 | |||
| 30 | registerRateLimiterComponent(t); | ||
| 31 | return t; | ||
| 28 | } | 32 | } |
| 29 | 33 | ||
| 30 | /** | 34 | /** |
| @@ -3,13 +3,17 @@ import { convexTest } from "convex-test"; · 2 unmodified lines | |||
| 3 | import { internal } from "./_generated/api"; | 3 | import { internal } from "./_generated/api"; |
| 4 | import type { Id } from "./_generated/dataModel.d.ts"; | 4 | import type { Id } from "./_generated/dataModel.d.ts"; |
| 5 | import schema from "./schema"; | 5 | import schema from "./schema"; |
| 6 | import { registerRateLimiterComponent } from "./testHelpers"; | ||
| 6 | 7 | ||
| 7 | function setup() { | 8 | function setup() { |
| 8 | return convexTest(schema, { | 9 | const t = convexTest(schema, { |
| 9 | "./database.ts": () => import("./database"), | 10 | "./database.ts": () => import("./database"), |
| 10 | "./toolRegistry.ts": () => import("./toolRegistry"), | 11 | "./toolRegistry.ts": () => import("./toolRegistry"), |
| 11 | "./_generated/api.js": () => import("./_generated/api.js"), | 12 | "./_generated/api.js": () => import("./_generated/api.js"), |
| 12 | }); | 13 | }); |
| 14 | |||
| 15 | registerRateLimiterComponent(t); | ||
| 16 | return t; | ||
| 13 | } | 17 | } |
| 14 | 18 | ||
| 15 | /** Insert a minimal workspace row and return its Id. */ | 19 | /** Insert a minimal workspace row and return its Id. */ |
| @@ -2,12 +2,13 @@ import { expect, test } from "bun:test"; · 1 unmodified line | |||
| 2 | import { convexTest } from "convex-test"; | 2 | import { convexTest } from "convex-test"; |
| 3 | import { internal } from "./_generated/api"; | 3 | import { internal } from "./_generated/api"; |
| 4 | import schema from "./schema"; | 4 | import schema from "./schema"; |
| 5 | import { registerRateLimiterComponent } from "./testHelpers"; | ||
| 5 | 6 | ||
| 6 | const GITHUB_OPENAPI_SPEC_URL = | 7 | const GITHUB_OPENAPI_SPEC_URL = |
| 7 | "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json?convex_test_profile=github"; | 8 | "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json?convex_test_profile=github"; |
| 8 | 9 | ||
| 9 | function setup() { | 10 | function setup() { |
| 10 | return convexTest(schema, { | 11 | const t = convexTest(schema, { |
| 11 | "./database.ts": () => import("./database"), | 12 | "./database.ts": () => import("./database"), |
| 12 | "./executorNode.ts": () => import("./executorNode"), | 13 | "./executorNode.ts": () => import("./executorNode"), |
| 13 | "./workspaceAuthInternal.ts": () => import("./workspaceAuthInternal"), | 14 | "./workspaceAuthInternal.ts": () => import("./workspaceAuthInternal"), |
| @@ -16,6 +17,9 @@ function setup() { · 2 unmodified lines | |||
| 16 | "./runtimeNode.ts": () => import("./runtimeNode"), | 17 | "./runtimeNode.ts": () => import("./runtimeNode"), |
| 17 | "./_generated/api.js": () => import("./_generated/api.js"), | 18 | "./_generated/api.js": () => import("./_generated/api.js"), |
| 18 | }); | 19 | }); |
| 20 | |||
| 21 | registerRateLimiterComponent(t); | ||
| 22 | return t; | ||
| 19 | } | 23 | } |
| 20 | 24 | ||
| 21 | test("convex-test keeps GitHub inventory build warm-cache fast", async () => { | 25 | test("convex-test keeps GitHub inventory build warm-cache fast", async () => { |
| @@ -2,13 +2,17 @@ import { expect, test, describe } from "bun:test"; · 1 unmodified line | |||
| 2 | import { convexTest } from "convex-test"; | 2 | import { convexTest } from "convex-test"; |
| 3 | import { internal } from "./_generated/api"; | 3 | import { internal } from "./_generated/api"; |
| 4 | import schema from "./schema"; | 4 | import schema from "./schema"; |
| 5 | import { registerRateLimiterComponent } from "./testHelpers"; | ||
| 5 | 6 | ||
| 6 | function setup() { | 7 | function setup() { |
| 7 | return convexTest(schema, { | 8 | const t = convexTest(schema, { |
| 8 | "./openApiSpecCache.ts": () => import("./openApiSpecCache"), | 9 | "./openApiSpecCache.ts": () => import("./openApiSpecCache"), |
| 9 | "./toolRegistry.ts": () => import("./toolRegistry"), | 10 | "./toolRegistry.ts": () => import("./toolRegistry"), |
| 10 | "./_generated/api.js": () => import("./_generated/api.js"), | 11 | "./_generated/api.js": () => import("./_generated/api.js"), |
| 11 | }); | 12 | }); |
| 13 | |||
| 14 | registerRateLimiterComponent(t); | ||
| 15 | return t; | ||
| 12 | } | 16 | } |
| 13 | 17 | ||
| 14 | describe("openApiSpecCache table operations", () => { | 18 | describe("openApiSpecCache table operations", () => { |
| @@ -0,0 +1,14 @@ | |||
| 1 | import type { TestConvex } from "convex-test"; | ||
| 2 | import { defineSchema } from "convex/server"; | ||
| 3 | import type { GenericSchema, SchemaDefinition } from "convex/server"; | ||
| 4 | |||
| 5 | const rateLimiterSchema = defineSchema({}); | ||
| 6 | |||
| 7 | const rateLimiterModules = { | ||
| 8 | "./lib.ts": () => import("../src/testing/rate_limiter_component"), | ||
| 9 | "./_generated/api.js": () => import("./_generated/api.js"), | ||
| 10 | }; | ||
| 11 | |||
| 12 | export function registerRateLimiterComponent(t: TestConvex<SchemaDefinition<GenericSchema, boolean>>): void { | ||
| 13 | t.registerComponent("rateLimiter", rateLimiterSchema, rateLimiterModules); | ||
| 14 | } | ||
| @@ -0,0 +1,72 @@ | |||
| 1 | import { mutationGeneric, queryGeneric } from "convex/server"; | ||
| 2 | import { v } from "convex/values"; | ||
| 3 | |||
| 4 | const limitArgs = { | ||
| 5 | name: v.string(), | ||
| 6 | config: v.any(), | ||
| 7 | key: v.optional(v.string()), | ||
| 8 | count: v.optional(v.number()), | ||
| 9 | reserve: v.optional(v.boolean()), | ||
| 10 | throws: v.optional(v.boolean()), | ||
| 11 | }; | ||
| 12 | |||
| 13 | const limitResult = v.object({ | ||
| 14 | ok: v.boolean(), | ||
| 15 | retryAfter: v.optional(v.number()), | ||
| 16 | }); | ||
| 17 | |||
| 18 | export const rateLimit = mutationGeneric({ | ||
| 19 | args: limitArgs, | ||
| 20 | returns: limitResult, | ||
| 21 | handler: async () => ({ ok: true }), | ||
| 22 | }); | ||
| 23 | |||
| 24 | export const checkRateLimit = queryGeneric({ | ||
| 25 | args: limitArgs, | ||
| 26 | returns: limitResult, | ||
| 27 | handler: async () => ({ ok: true }), | ||
| 28 | }); | ||
| 29 | |||
| 30 | export const resetRateLimit = mutationGeneric({ | ||
| 31 | args: { | ||
| 32 | name: v.string(), | ||
| 33 | key: v.optional(v.string()), | ||
| 34 | }, | ||
| 35 | returns: v.null(), | ||
| 36 | handler: async () => null, | ||
| 37 | }); | ||
| 38 | |||
| 39 | export const getValue = queryGeneric({ | ||
| 40 | args: { | ||
| 41 | name: v.string(), | ||
| 42 | config: v.any(), | ||
| 43 | key: v.optional(v.string()), | ||
| 44 | sampleShards: v.optional(v.number()), | ||
| 45 | }, | ||
| 46 | returns: v.object({ | ||
| 47 | config: v.any(), | ||
| 48 | shard: v.number(), | ||
| 49 | ts: v.number(), | ||
| 50 | value: v.number(), | ||
| 51 | }), | ||
| 52 | handler: async (_ctx, args) => ({ | ||
| 53 | config: args.config, | ||
| 54 | shard: 0, | ||
| 55 | ts: Date.now(), | ||
| 56 | value: 0, | ||
| 57 | }), | ||
| 58 | }); | ||
| 59 | |||
| 60 | export const clearAll = mutationGeneric({ | ||
| 61 | args: { | ||
| 62 | before: v.optional(v.number()), | ||
| 63 | }, | ||
| 64 | returns: v.null(), | ||
| 65 | handler: async () => null, | ||
| 66 | }); | ||
| 67 | |||
| 68 | export const getServerTime = mutationGeneric({ | ||
| 69 | args: {}, | ||
| 70 | returns: v.number(), | ||
| 71 | handler: async () => Date.now(), | ||
| 72 | }); | ||