Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/modal-backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ markers = [
# PR that drops the total below this FAILS; a PR that raises coverage bumps
# the floor. Never lower it to make a PR pass — that defeats the ratchet.
[tool.coverage.report]
fail_under = 84
fail_under = 85

[tool.mypy]
python_version = "3.12"
Expand Down
111 changes: 111 additions & 0 deletions apps/modal-backend/tests/test_view_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,114 @@ def test_parse_view_confidence_coercion() -> None:
assert parse_view({"level": "map", "projection": "top_down",
"pitch_deg": -90, "confidence": "junk"})["confidence"] == 0.5
assert DEFAULT_VIEW["confidence"] == 0.0


# ── estimate_view (the one-VLM-call path; mocked create, no live calls) ───────


def _fake_reply(content: str, finish_reason: str = "stop"):
from types import SimpleNamespace

return SimpleNamespace(
choices=[
SimpleNamespace(
message=SimpleNamespace(content=content), finish_reason=finish_reason
)
]
)


async def test_estimate_view_success_parses_and_sends_the_image(monkeypatch) -> None:
from providers import llm
from providers.view_estimator import estimate_view

captured: dict = {}

async def fake_create(client, **kwargs):
captured.update(kwargs)
return _fake_reply(
'{"level":"street","projection":"perspective","pitch_deg":-10,'
'"scale_tier":"district","confidence":0.9}'
)

monkeypatch.setattr(llm, "_create_with_retry", fake_create)
monkeypatch.setattr(llm, "_client", lambda: object())
out = await estimate_view(b"img-bytes", caption="a cobbled lane")
assert out == {
"level": "street",
"projection": "perspective",
"pitch_deg": -10.0,
"scale_tier": "district",
"confidence": 0.9,
}
user_parts = captured["messages"][1]["content"]
assert 'Caption: "a cobbled lane"' in user_parts[0]["text"]
assert user_parts[1]["image_url"]["url"].startswith("data:image/jpeg;base64,")
assert captured["temperature"] == 0.0


async def test_estimate_view_omits_the_caption_clause_when_absent(monkeypatch) -> None:
from providers import llm
from providers.view_estimator import estimate_view

captured: dict = {}

async def fake_create(client, **kwargs):
captured.update(kwargs)
return _fake_reply('{"level":"map","projection":"top_down","pitch_deg":-90}')

monkeypatch.setattr(llm, "_create_with_retry", fake_create)
monkeypatch.setattr(llm, "_client", lambda: object())
out = await estimate_view(b"img")
assert out["level"] == "map"
assert captured["messages"][1]["content"][0]["text"] == "Classify this image's camera."


async def test_estimate_view_unparseable_reply_degrades_to_default_and_warns(
monkeypatch,
) -> None:
import obs
from providers import llm
from providers.view_estimator import DEFAULT_VIEW, estimate_view

events: list[tuple[str, str]] = []

async def fake_create(client, **kwargs):
return _fake_reply("I cannot classify that image.", finish_reason="stop")

monkeypatch.setattr(llm, "_create_with_retry", fake_create)
monkeypatch.setattr(llm, "_client", lambda: object())
monkeypatch.setattr(obs, "log", lambda level, event, **kw: events.append((level, event)))
assert await estimate_view(b"img") == DEFAULT_VIEW
assert ("warn", "view.parse_failed") in events


async def test_estimate_view_upstream_failure_degrades_to_default_and_warns(
monkeypatch,
) -> None:
import obs
from providers import llm
from providers.view_estimator import DEFAULT_VIEW, estimate_view

events: list[tuple[str, str]] = []

async def boom(client, **kwargs):
raise RuntimeError("upstream 502")

monkeypatch.setattr(llm, "_create_with_retry", boom)
monkeypatch.setattr(llm, "_client", lambda: object())
monkeypatch.setattr(obs, "log", lambda level, event, **kw: events.append((level, event)))
assert await estimate_view(b"img") == DEFAULT_VIEW
assert ("warn", "view.estimate_failed") in events


def test_model_env_override(monkeypatch) -> None:
from providers.view_estimator import _model

monkeypatch.delenv("WORLD_BENCH_JUDGE_MODEL", raising=False)
monkeypatch.delenv("OPENROUTER_VLM_MODEL", raising=False)
assert _model() == "google/gemini-3-flash-preview"
monkeypatch.setenv("OPENROUTER_VLM_MODEL", "qwen/qwen-vl")
assert _model() == "qwen/qwen-vl"
monkeypatch.setenv("WORLD_BENCH_JUDGE_MODEL", "judge/x")
assert _model() == "judge/x"
158 changes: 153 additions & 5 deletions apps/web/lib/session-owner.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

// In-memory stand-in for the `session_owners` collection: insertOne enforces the
// unique _id (throws 11000 on a dup, like Mongo), findOne reads it back.
// unique _id (throws 11000 on a dup, like Mongo), findOne reads it back. The
// knobs simulate the two failure modes claimOrVerify must distinguish: an
// unrelated insert error (rethrown) and a lost delete race (dup + missing doc).
const store = new Map<string, { _id: string; owner_token: string }>();
const knobs = { failInsert: null as "generic" | "dup" | null, findNull: false };
const fakeCollection = {
async insertOne(doc: { _id: string; owner_token: string }) {
if (store.has(doc._id)) {
if (knobs.failInsert === "generic") throw new Error("boom");
if (knobs.failInsert === "dup" || store.has(doc._id)) {
const err = new Error("dup") as Error & { code?: number };
err.code = 11000;
throw err;
Expand All @@ -14,6 +18,7 @@ const fakeCollection = {
return { acknowledged: true };
},
async findOne(filter: { _id: string }) {
if (knobs.findNull) return null;
return store.get(filter._id) ?? null;
},
};
Expand All @@ -22,10 +27,29 @@ vi.mock("./db", () => ({
getDb: async () => ({ collection: () => fakeCollection }),
}));

import { claimOrVerify } from "./session-owner";
// Cookie jar behind the mocked next/headers (route-handler cookies() store).
const jar = vi.hoisted(() => ({
token: null as string | null,
sets: [] as { name: string; value: string; opts: Record<string, unknown> }[],
}));
vi.mock("next/headers", () => ({
cookies: async () => ({
get: (name: string) => (jar.token ? { name, value: jar.token } : undefined),
set: (name: string, value: string, opts: Record<string, unknown>) => {
jar.sets.push({ name, value, opts });
jar.token = value;
},
}),
}));

import { claimOrVerify, requireOwner, verifyOwnerReadonly } from "./session-owner";

describe("claimOrVerify (session ownership)", () => {
beforeEach(() => store.clear());
beforeEach(() => {
store.clear();
knobs.failInsert = null;
knobs.findNull = false;
});

it("first caller claims an unowned session", async () => {
expect(await claimOrVerify("s1", "tokenA")).toBe("ok");
Expand All @@ -47,4 +71,128 @@ describe("claimOrVerify (session ownership)", () => {
expect(await claimOrVerify("legacy", "firstSeen")).toBe("ok");
expect(await claimOrVerify("legacy", "stranger")).toBe("forbidden");
});

it("an unrelated insert error is rethrown, not swallowed as a dup", async () => {
knobs.failInsert = "generic";
await expect(claimOrVerify("s1", "tokenA")).rejects.toThrow("boom");
});

it("a lost delete race (dup insert, doc gone on re-read) is claimable", async () => {
knobs.failInsert = "dup";
knobs.findNull = true;
expect(await claimOrVerify("s1", "tokenA")).toBe("ok");
});
});

// ── The route gates (cookie mint + claim/verify + wire errors) ───────────────

const CONFIGURED = () => {
vi.stubEnv("MONGODB_URI", "mongodb://test");
vi.stubEnv("MONGODB_DB", "ofb_test");
};

describe("requireOwner (mutation gate)", () => {
beforeEach(() => {
store.clear();
knobs.failInsert = null;
knobs.findNull = false;
jar.token = null;
jar.sets.length = 0;
});
afterEach(() => vi.unstubAllEnvs());

it("rejects an unsafe session id with a 400 before touching anything", async () => {
CONFIGURED();
const out = await requireOwner("nope; drop table");
expect(out.ok).toBe(false);
if (!out.ok) {
expect(out.res.status).toBe(400);
expect(await out.res.json()).toEqual({ error: "invalid session id" });
}
expect(jar.sets).toHaveLength(0);
});

it("no persistence configured → open (demo mode must not crash)", async () => {
vi.stubEnv("MONGODB_URI", "");
vi.stubEnv("MONGODB_DB", "");
expect(await requireOwner("session_1")).toEqual({ ok: true });
expect(jar.sets).toHaveLength(0);
expect(store.size).toBe(0);
});

it("first write mints an httpOnly cookie and claims the session", async () => {
CONFIGURED();
const out = await requireOwner("session_1");
expect(out.ok).toBe(true);
expect(jar.sets).toHaveLength(1);
const cookie = jar.sets[0]!;
expect(cookie.name).toBe("ofb_owner");
expect(cookie.opts.httpOnly).toBe(true);
expect(cookie.opts.sameSite).toBe("lax");
expect(store.get("session_1")?.owner_token).toBe(cookie.value);
});

it("the owner's later mutations pass without re-minting", async () => {
CONFIGURED();
jar.token = "mine";
await claimOrVerify("session_1", "mine");
expect(await requireOwner("session_1")).toEqual({ ok: true });
expect(jar.sets).toHaveLength(0); // existing cookie reused
});

it("a stranger's cookie is rejected with the friendly 403", async () => {
CONFIGURED();
await claimOrVerify("session_1", "owner-token");
jar.token = "stranger-token";
const out = await requireOwner("session_1");
expect(out.ok).toBe(false);
if (!out.ok) {
expect(out.res.status).toBe(403);
expect(await out.res.json()).toEqual({
error: "this session belongs to another browser",
});
}
});
});

describe("verifyOwnerReadonly (streaming-route gate: never claims, never mints)", () => {
beforeEach(() => {
store.clear();
knobs.failInsert = null;
knobs.findNull = false;
jar.token = null;
jar.sets.length = 0;
});
afterEach(() => vi.unstubAllEnvs());

it("unsafe id → 400; unconfigured store → open", async () => {
CONFIGURED();
const bad = await verifyOwnerReadonly("../etc/passwd");
expect(bad.ok).toBe(false);
if (!bad.ok) expect(bad.res.status).toBe(400);
vi.stubEnv("MONGODB_URI", "");
expect(await verifyOwnerReadonly("session_1")).toEqual({ ok: true });
});

it("an unowned session is allowed and left unclaimed (claim happens on write)", async () => {
CONFIGURED();
expect(await verifyOwnerReadonly("session_1")).toEqual({ ok: true });
expect(store.size).toBe(0); // no claim
expect(jar.sets).toHaveLength(0); // no cookie mint
});

it("an owned session admits the matching cookie and 403s everyone else", async () => {
CONFIGURED();
await claimOrVerify("session_1", "owner-token");
jar.token = "owner-token";
expect(await verifyOwnerReadonly("session_1")).toEqual({ ok: true });
jar.token = "stranger";
const wrong = await verifyOwnerReadonly("session_1");
expect(wrong.ok).toBe(false);
if (!wrong.ok) expect(wrong.res.status).toBe(403);
jar.token = null; // no cookie at all → same refusal
const none = await verifyOwnerReadonly("session_1");
expect(none.ok).toBe(false);
if (!none.ok) expect(none.res.status).toBe(403);
});
});
Loading
Loading