mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-11-05 07:39:44 +00:00
better-fetch parses request bodies through the input schema, so the missing dreaming field was stripped before requests left the client. Add the field to single and batch add schemas and cover it with tests. Refs #1851
208 lines
5.6 KiB
TypeScript
208 lines
5.6 KiB
TypeScript
import { describe, expect, it } from "bun:test"
|
|
import { readFileSync } from "node:fs"
|
|
import {
|
|
BatchAddDocumentsRequestSchema,
|
|
BulkDeleteMemoriesSchema,
|
|
DocumentsWithMemoriesQuerySchema,
|
|
ListMemoriesQuerySchema,
|
|
MemoryAddSchema,
|
|
SearchRequestSchema,
|
|
Searchv4RequestSchema,
|
|
} from "./api"
|
|
|
|
describe("search threshold schemas", () => {
|
|
it("do not contain redundant number transforms or unreachable range guards", () => {
|
|
const source = readFileSync(new URL("./api.ts", import.meta.url), "utf8")
|
|
const searchSchemas = source.slice(
|
|
source.indexOf("export const SearchRequestSchema"),
|
|
source.indexOf("export const SearchResultSchema"),
|
|
)
|
|
|
|
expect(searchSchemas).not.toContain(".transform(Number)")
|
|
expect(searchSchemas).not.toContain("v === undefined || (v >= 0 && v <= 1)")
|
|
})
|
|
|
|
it("preserves threshold defaults", () => {
|
|
const search = SearchRequestSchema.parse({ q: "memory" })
|
|
const searchV4 = Searchv4RequestSchema.parse({ q: "memory" })
|
|
|
|
expect(search.chunkThreshold).toBe(0)
|
|
expect(search.documentThreshold).toBe(0)
|
|
expect(searchV4.threshold).toBe(0.6)
|
|
})
|
|
|
|
it.each([0, 0.5, 1])("accepts inclusive threshold value %p", (threshold) => {
|
|
expect(
|
|
SearchRequestSchema.parse({
|
|
q: "memory",
|
|
chunkThreshold: threshold,
|
|
documentThreshold: threshold,
|
|
}),
|
|
).toMatchObject({
|
|
chunkThreshold: threshold,
|
|
documentThreshold: threshold,
|
|
})
|
|
expect(
|
|
Searchv4RequestSchema.parse({ q: "memory", threshold }).threshold,
|
|
).toBe(threshold)
|
|
})
|
|
|
|
it.each([
|
|
-0.1, 1.1,
|
|
])("rejects out-of-range threshold value %p", (threshold) => {
|
|
expect(
|
|
SearchRequestSchema.safeParse({
|
|
q: "memory",
|
|
chunkThreshold: threshold,
|
|
}).success,
|
|
).toBe(false)
|
|
expect(
|
|
SearchRequestSchema.safeParse({
|
|
q: "memory",
|
|
documentThreshold: threshold,
|
|
}).success,
|
|
).toBe(false)
|
|
expect(
|
|
Searchv4RequestSchema.safeParse({ q: "memory", threshold }).success,
|
|
).toBe(false)
|
|
})
|
|
|
|
it("does not coerce threshold strings", () => {
|
|
expect(
|
|
SearchRequestSchema.safeParse({
|
|
q: "memory",
|
|
chunkThreshold: "0.5",
|
|
}).success,
|
|
).toBe(false)
|
|
expect(
|
|
SearchRequestSchema.safeParse({
|
|
q: "memory",
|
|
documentThreshold: "0.5",
|
|
}).success,
|
|
).toBe(false)
|
|
expect(
|
|
Searchv4RequestSchema.safeParse({
|
|
q: "memory",
|
|
threshold: "0.5",
|
|
}).success,
|
|
).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe("pagination query schemas", () => {
|
|
it("preserve page/limit defaults", () => {
|
|
const listed = ListMemoriesQuerySchema.parse({})
|
|
expect(listed.page).toBe(1)
|
|
expect(listed.limit).toBe(10)
|
|
|
|
const docs = DocumentsWithMemoriesQuerySchema.parse({})
|
|
expect(docs.page).toBe(1)
|
|
expect(docs.limit).toBe(10)
|
|
})
|
|
|
|
it.each([
|
|
1, 50, 1100,
|
|
])("ListMemoriesQuerySchema accepts numeric limit %p", (limit) => {
|
|
expect(ListMemoriesQuerySchema.parse({ limit }).limit).toBe(limit)
|
|
})
|
|
|
|
it("ListMemoriesQuerySchema accepts numeric string page/limit", () => {
|
|
const parsed = ListMemoriesQuerySchema.parse({ page: "3", limit: "25" })
|
|
expect(parsed.page).toBe(3)
|
|
expect(parsed.limit).toBe(25)
|
|
})
|
|
|
|
it.each([
|
|
0, -5, 2.5,
|
|
])("ListMemoriesQuerySchema rejects non-positive or fractional numeric limit %p", (limit) => {
|
|
expect(ListMemoriesQuerySchema.safeParse({ limit }).success).toBe(false)
|
|
})
|
|
|
|
it.each([
|
|
0, -1, 1.5,
|
|
])("ListMemoriesQuerySchema rejects non-positive or fractional numeric page %p", (page) => {
|
|
expect(ListMemoriesQuerySchema.safeParse({ page }).success).toBe(false)
|
|
})
|
|
|
|
it("ListMemoriesQuerySchema still caps limit at 1100", () => {
|
|
expect(ListMemoriesQuerySchema.safeParse({ limit: 1101 }).success).toBe(
|
|
false,
|
|
)
|
|
})
|
|
|
|
it.each([
|
|
0, -1, 2.5,
|
|
])("DocumentsWithMemoriesQuerySchema rejects invalid page %p", (page) => {
|
|
expect(DocumentsWithMemoriesQuerySchema.safeParse({ page }).success).toBe(
|
|
false,
|
|
)
|
|
})
|
|
|
|
it.each([
|
|
0, -10, 2.5,
|
|
])("DocumentsWithMemoriesQuerySchema rejects invalid limit %p", (limit) => {
|
|
expect(DocumentsWithMemoriesQuerySchema.safeParse({ limit }).success).toBe(
|
|
false,
|
|
)
|
|
})
|
|
|
|
it("DocumentsWithMemoriesQuerySchema accepts a normal request", () => {
|
|
const parsed = DocumentsWithMemoriesQuerySchema.parse({
|
|
page: 2,
|
|
limit: 50,
|
|
})
|
|
expect(parsed.page).toBe(2)
|
|
expect(parsed.limit).toBe(50)
|
|
})
|
|
|
|
it("DocumentsWithMemoriesQuerySchema caps limit at 1000", () => {
|
|
expect(
|
|
DocumentsWithMemoriesQuerySchema.safeParse({ limit: 1001 }).success,
|
|
).toBe(false)
|
|
expect(
|
|
DocumentsWithMemoriesQuerySchema.safeParse({ limit: 200 }).success,
|
|
).toBe(true)
|
|
})
|
|
|
|
it("BulkDeleteMemoriesSchema caps containerTags at 100 entries of bounded length", () => {
|
|
const tooMany = {
|
|
containerTags: Array.from({ length: 101 }, (_, i) => `tag_${i}`),
|
|
}
|
|
expect(BulkDeleteMemoriesSchema.safeParse(tooMany).success).toBe(false)
|
|
|
|
const tagTooLong = { containerTags: ["x".repeat(257)] }
|
|
expect(BulkDeleteMemoriesSchema.safeParse(tagTooLong).success).toBe(false)
|
|
|
|
const ok = { containerTags: ["tag_a", "tag_b"] }
|
|
expect(BulkDeleteMemoriesSchema.safeParse(ok).success).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe("document add input schemas", () => {
|
|
it("preserves dreaming on a single document add", () => {
|
|
const parsed = MemoryAddSchema.parse({
|
|
content: "probe",
|
|
dreaming: "instant",
|
|
})
|
|
|
|
expect(parsed).toEqual({ content: "probe", dreaming: "instant" })
|
|
})
|
|
|
|
it("preserves dreaming on a batch document add", () => {
|
|
const parsed = BatchAddDocumentsRequestSchema.parse({
|
|
documents: [{ content: "probe" }],
|
|
dreaming: "dynamic",
|
|
})
|
|
|
|
expect(parsed).toEqual({
|
|
documents: [{ content: "probe" }],
|
|
dreaming: "dynamic",
|
|
})
|
|
})
|
|
|
|
it("rejects an invalid dreaming mode", () => {
|
|
expect(() =>
|
|
MemoryAddSchema.parse({ content: "probe", dreaming: "later" }),
|
|
).toThrow()
|
|
})
|
|
})
|