History

fix: reconcile main's round-18 features after the rebase

Adopts the round's main features into the v2 architecture: the #9023 media rework (streamed videos, media-file assets, protocol-relative links), #9098 shared live-activity row folded into the v2 working and thinking rows, the #9084/#9078 Claude model catalog for v2 consumers, a native #9005 OpenCode child-session abort in the v2 adapter, #9013's landed LegendList patch, and per-environment sidebar provider entries. For #8600 the server-side pieces land, but auto-settle evaluation stays client-side (reading the new server-owned settings) until the v2 orchestrator grows its own settlement reactor; main's v1-only reactor and coalescer additions are dropped with the rest of the v1 path.

Julius Marminge committed 2 months ago

Commit 7b13bca1 parent 786a25847 files · +1855 −2616Browse files

Showing the changes to apps/server/src/http.test.ts (1 of 47 files). Show all

Unified · Split

apps/server/src/http.test.ts+177−3
@@ -1,12 +1,13 @@
1import { expect, it } from "@effect/vitest";1import { expect, it } from "@effect/vitest";
2import { describe, vi } from "vite-plus/test";
3import * as NodeHttpPlatform from "@effect/platform-node/NodeHttpPlatform";
4import * as NodeServices from "@effect/platform-node/NodeServices";
2import * as Effect from "effect/Effect";5import * as Effect from "effect/Effect";
3import * as FileSystem from "effect/FileSystem";6import * as FileSystem from "effect/FileSystem";
4import * as Layer from "effect/Layer";7import * as Layer from "effect/Layer";
5import * as Path from "effect/Path";8import * as Path from "effect/Path";
6import { HttpRouter, HttpServerResponse } from "effect/unstable/http";9import { HttpRouter, HttpServerResponse } from "effect/unstable/http";
7import { describe } from "vite-plus/test";10import { openMediaFile } from "./assets/MediaFile.ts";
8import * as NodeHttpPlatform from "@effect/platform-node/NodeHttpPlatform";
9import * as NodeServices from "@effect/platform-node/NodeServices";
10 11
11import { ORCHESTRATION_PROTOCOL_HEADER } from "@t3tools/contracts";12import { ORCHESTRATION_PROTOCOL_HEADER } from "@t3tools/contracts";
12 13
@@ -70,6 +71,179 @@ describe("browser API CORS", () => { · 57 unmodified lines
70const fileResponseLayer = Layer.mergeAll(NodeHttpPlatform.layer, NodeServices.layer);71const fileResponseLayer = Layer.mergeAll(NodeHttpPlatform.layer, NodeServices.layer);
71 72
72describe("video asset byte ranges", () => {73describe("video asset byte ranges", () => {
74 it.effect("uses current descriptor metadata after an in-place truncate or extension", () =>
75 Effect.gen(function* () {
76 const fs = yield* FileSystem.FileSystem;
77 const path = yield* Path.Path;
78 const directory = yield* fs.makeTempDirectoryScoped({ prefix: "t3-guarded-current-stat-" });
79 const filePath = path.join(directory, "clip.mp4");
80 for (const [contents, range, method, expected, status, contentRange] of [
81 ["1234", undefined, "GET", "1234", 200, null],
82 ["0123456789abcdef", undefined, "GET", "0123456789abcdef", 200, null],
83 ["1234", "bytes=4-", "GET", "", 416, "bytes */4"],
84 ["1234", "bytes=1-20", "GET", "234", 206, "bytes 1-3/4"],
85 ["0123456789abcdef", "bytes=10-", "GET", "abcdef", 206, "bytes 10-15/16"],
86 ["0123456789abcdef", undefined, "HEAD", "", 200, null],
87 ["", undefined, "GET", "", 200, null],
88 ["", "bytes=0-1", "GET", "", 416, "bytes */0"],
89 ] as const) {
90 yield* fs.writeFileString(filePath, "0123456789");
91 const canonicalPath = yield* fs.realPath(filePath);
92 const file = yield* openMediaFile(canonicalPath);
93 if (!file) throw new Error("Expected an opened media file");
94 yield* fs.writeFileString(filePath, contents);
95 const response = HttpServerResponse.toWeb(
96 yield* assetFileResponse(
97 { path: canonicalPath, file, mimeType: "video/mp4" },
98 range,
99 undefined,
100 method,
101 ),
102 );
103 expect(response.status).toBe(status);
104 expect(response.headers.get("content-range")).toBe(contentRange);
105 if (status !== 416) {
106 expect(response.headers.get("content-length")).toBe(
107 String(method === "HEAD" ? contents.length : expected.length),
108 );
109 }
110 expect(yield* Effect.promise(() => response.text())).toBe(expected);
111 }
112 }).pipe(Effect.provide(fileResponseLayer)),
113 );
114
115 it.effect(
116 "rejects unaddressable ranges before streaming and preserves small ranges on large files",
117 () =>
118 Effect.gen(function* () {
119 const fs = yield* FileSystem.FileSystem;
120 const path = yield* Path.Path;
121 const directory = yield* fs.makeTempDirectoryScoped({ prefix: "t3-guarded-offset-limit-" });
122 const filePath = path.join(directory, "clip.mp4");
123 yield* fs.writeFileString(filePath, "0123456789");
124 const canonicalPath = yield* fs.realPath(filePath);
125 const unsafeOffset = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
126 const size = unsafeOffset + 32n;
127 for (const [range, status] of [
128 [`bytes=${unsafeOffset}-${unsafeOffset}`, 416],
129 [`bytes=0-${unsafeOffset}`, 416],
130 ["bytes=-1", 416],
131 [undefined, 413],
132 ["bytes=0-1", 206],
133 ] as const) {
134 const file = yield* openMediaFile(canonicalPath);
135 if (!file) throw new Error("Expected an opened media file");
136 // Model a sparse file beyond the native stream's numeric addressing limit.
137 const info = yield* Effect.promise(() => file.handle.stat({ bigint: true }));
138 info.size = size;
139 const statSpy = vi.spyOn(file.handle, "stat").mockResolvedValue(info);
140 yield* Effect.addFinalizer(() => Effect.sync(() => statSpy.mockRestore()));
141 const response = HttpServerResponse.toWeb(
142 yield* assetFileResponse({ path: canonicalPath, file, mimeType: "video/mp4" }, range),
143 );
144 expect(response.status).toBe(status);
145 if (status === 416) {
146 expect(response.headers.get("content-range")).toBe(`bytes */${size}`);
147 expect(yield* Effect.promise(() => response.text())).toBe("");
148 } else if (status === 206) {
149 expect(response.headers.get("content-range")).toBe(`bytes 0-1/${size}`);
150 expect(yield* Effect.promise(() => response.text())).toBe("01");
151 } else {
152 expect(yield* Effect.promise(() => response.text())).toBe(
153 "File is too large to preview.",
154 );
155 }
156 }
157 }).pipe(Effect.provide(fileResponseLayer)),
158 );
159
160 it.effect("streams guarded file ranges, including suffixes and conditional requests", () =>
161 Effect.gen(function* () {
162 const fs = yield* FileSystem.FileSystem;
163 const path = yield* Path.Path;
164 const directory = yield* fs.makeTempDirectoryScoped({ prefix: "t3-guarded-range-" });
165 const filePath = path.join(directory, "clip.mp4");
166 yield* fs.writeFileString(filePath, "0123456789");
167 const canonicalPath = yield* fs.realPath(filePath);
168 for (const [range, ifRange, expected, status, contentRange] of [
169 [undefined, undefined, "0123456789", 200, null],
170 ["bytes=0-1", undefined, "01", 206, "bytes 0-1/10"],
171 ["bytes=4-", undefined, "456789", 206, "bytes 4-9/10"],
172 ["bytes=-3", undefined, "789", 206, "bytes 7-9/10"],
173 ["bytes=-999999999999999999999999", undefined, "0123456789", 206, "bytes 0-9/10"],
174 ["bytes=10-", undefined, "", 416, "bytes */10"],
175 ["bytes=0-1", '"old-etag"', "0123456789", 200, null],
176 ["bytes=0-1", "", "0123456789", 200, null],
177 ] as const) {
178 const file = yield* openMediaFile(canonicalPath);
179 if (!file) throw new Error("Expected an opened media file");
180 const response = HttpServerResponse.toWeb(
181 yield* assetFileResponse(
182 { path: canonicalPath, file, mimeType: "video/mp4" },
183 range,
184 ifRange,
185 ),
186 );
187 expect(response.status).toBe(status);
188 expect(response.headers.get("accept-ranges")).toBe("bytes");
189 expect(response.headers.get("content-range")).toBe(contentRange);
190 expect(response.headers.get("cache-control")).toBe("private, no-store");
191 expect(response.headers.get("etag")).toBeNull();
192 expect(response.headers.get("last-modified")).toBeNull();
193 if (status !== 416)
194 expect(response.headers.get("content-length")).toBe(String(expected.length));
195 expect(yield* Effect.promise(() => response.text())).toBe(expected);
196 }
197 }).pipe(Effect.provide(fileResponseLayer)),
198 );
199
200 it.effect("closes guarded descriptors after full, HEAD, rejected, and cancelled responses", () =>
201 Effect.gen(function* () {
202 const fs = yield* FileSystem.FileSystem;
203 const path = yield* Path.Path;
204 const directory = yield* fs.makeTempDirectoryScoped({ prefix: "t3-guarded-cleanup-" });
205 const filePath = path.join(directory, "clip.mp4");
206 const bytes = new Uint8Array(1024 * 1024).fill(42);
207 yield* fs.writeFile(filePath, bytes);
208 const canonicalPath = yield* fs.realPath(filePath);
209 for (const mode of ["full", "HEAD", "rejected", "cancelled"] as const) {
210 const file = yield* Effect.scoped(
211 Effect.gen(function* () {
212 const file = yield* openMediaFile(canonicalPath);
213 if (!file) throw new Error("Expected an opened media file");
214 const response = HttpServerResponse.toWeb(
215 yield* assetFileResponse(
216 { path: canonicalPath, file, mimeType: "video/mp4" },
217 mode === "rejected" ? `bytes=${bytes.length}-` : "bytes=0-",
218 undefined,
219 mode === "HEAD" ? "HEAD" : "GET",
220 ),
221 );
222 if (mode === "HEAD") {
223 expect(response.status).toBe(200);
224 expect(response.headers.get("content-length")).toBe(String(bytes.length));
225 expect(response.headers.get("content-range")).toBeNull();
226 expect(yield* Effect.promise(() => response.text())).toBe("");
227 } else if (mode === "rejected") {
228 expect(response.status).toBe(416);
229 expect(yield* Effect.promise(() => response.text())).toBe("");
230 } else if (mode === "cancelled") {
231 const reader = response.body!.getReader();
232 const first = yield* Effect.promise(() => reader.read());
233 expect(first.done).toBe(false);
234 expect(first.value!.byteLength).toBeLessThan(bytes.length);
235 yield* Effect.promise(() => reader.cancel());
236 } else {
237 expect(yield* Effect.promise(() => response.arrayBuffer())).toEqual(bytes.buffer);
238 }
239 return file;
240 }),
241 );
242 expect(file.handle.fd).toBe(-1);
243 }
244 }).pipe(Effect.provide(fileResponseLayer)),
245 );
246
73 it.effect("streams exactly the requested bytes and leaves full downloads intact", () =>247 it.effect("streams exactly the requested bytes and leaves full downloads intact", () =>
74 Effect.gen(function* () {248 Effect.gen(function* () {
75 const fs = yield* FileSystem.FileSystem;249 const fs = yield* FileSystem.FileSystem;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t