mirror of
https://github.com/anomalyco/opencode.git
synced 2026-10-28 19:52:53 +00:00
424 lines
20 KiB
TypeScript
424 lines
20 KiB
TypeScript
/**
|
|
* Portions adapted from web-platform-tests at revision 863077959ca8c1a7ceecfbe2534b75d2527b9013:
|
|
* - html/webappapis/atob/base64.any.js (btoa reference encoder, input list, and atob WebIDL cases)
|
|
* - fetch/data-urls/resources/base64.json (copied to fixtures/wpt-base64.json)
|
|
* - WebCryptoAPI/randomUUID.https.any.js
|
|
* - fetch/api/headers/{headers-basic,headers-errors}.any.js
|
|
*
|
|
* Copyright © web-platform-tests contributors. Governed by the 3-Clause BSD license in LICENSE.wpt.
|
|
*
|
|
* `assert_throws_dom("InvalidCharacterError", …)` becomes a check for a TypeError: CodeMode has no DOMException.
|
|
* `checkIteratorProperties` (prototype chain and property descriptors) and the custom iterator on a Headers
|
|
* instance are left out.
|
|
*/
|
|
import { describe, expect, test } from "bun:test"
|
|
import { Effect } from "effect"
|
|
import { CodeMode } from "../src/index.js"
|
|
|
|
const base64Cases = (await Bun.file(new URL("./fixtures/wpt-base64.json", import.meta.url)).json()) as Array<
|
|
[string, Array<number> | null]
|
|
>
|
|
|
|
const value = async (code: string) => {
|
|
const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
|
|
if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
|
|
return result.value
|
|
}
|
|
|
|
// The reference encoder from base64.any.js, run inside the interpreter so btoa is checked against
|
|
// an independent implementation rather than against the host's btoa.
|
|
const referenceEncoder = `
|
|
function btoaLookup(idx) {
|
|
if (idx < 26) return String.fromCharCode(idx + "A".charCodeAt(0))
|
|
if (idx < 52) return String.fromCharCode(idx - 26 + "a".charCodeAt(0))
|
|
if (idx < 62) return String.fromCharCode(idx - 52 + "0".charCodeAt(0))
|
|
if (idx == 62) return "+"
|
|
if (idx == 63) return "/"
|
|
}
|
|
function mybtoa(s) {
|
|
s = String(s)
|
|
for (var i = 0; i < s.length; i++) if (s.charCodeAt(i) > 255) return "INVALID_CHARACTER_ERR"
|
|
var out = ""
|
|
for (var i = 0; i < s.length; i += 3) {
|
|
var groupsOfSix = [undefined, undefined, undefined, undefined]
|
|
groupsOfSix[0] = s.charCodeAt(i) >> 2
|
|
groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4
|
|
if (s.length > i + 1) {
|
|
groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4
|
|
groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2
|
|
}
|
|
if (s.length > i + 2) {
|
|
groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6
|
|
groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f
|
|
}
|
|
for (var j = 0; j < groupsOfSix.length; j++) {
|
|
out += typeof groupsOfSix[j] == "undefined" ? "=" : btoaLookup(groupsOfSix[j])
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
function testBtoa(input) {
|
|
var expected = mybtoa(input)
|
|
if (expected === "INVALID_CHARACTER_ERR") {
|
|
try { btoa(input) } catch (error) { return error instanceof TypeError ? "ok" : error.name }
|
|
return "did not throw"
|
|
}
|
|
if (btoa(input) !== expected) return "btoa mismatch"
|
|
if (atob(btoa(input)) !== String(input)) return "roundtrip mismatch"
|
|
return "ok"
|
|
}
|
|
`
|
|
|
|
describe("btoa WPT parity (html/webappapis/atob/base64.any.js)", () => {
|
|
test("every input encodes like the reference encoder and round-trips through atob", async () => {
|
|
expect(
|
|
await value(`
|
|
${referenceEncoder}
|
|
var tests = ["עברית", "", "ab", "abc", "abcd", "abcde", "\\xff\\xff\\xc0", "\\0a", "a\\0b",
|
|
undefined, null, 7, 12, 1.5, true, false, NaN, +Infinity, -Infinity, 0, -0]
|
|
for (var i = 0; i < 258; i++) tests.push(String.fromCharCode(i))
|
|
tests.push(String.fromCharCode(10000), String.fromCharCode(65534), String.fromCharCode(65535))
|
|
tests.push(String.fromCharCode(0xd800, 0xdc00))
|
|
var everything = ""
|
|
for (var i = 0; i < 256; i++) everything += String.fromCharCode(i)
|
|
tests.push(everything)
|
|
return tests.map(testBtoa).filter((outcome) => outcome !== "ok")
|
|
`),
|
|
).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe("atob WPT parity (fetch/data-urls/resources/base64.json)", () => {
|
|
const idlCases: Array<[unknown, Array<number> | null]> = [
|
|
[undefined, null],
|
|
[null, [158, 233, 101]],
|
|
[7, null],
|
|
[12, [215]],
|
|
[1.5, null],
|
|
[true, [182, 187]],
|
|
[false, null],
|
|
[NaN, [53, 163]],
|
|
[Infinity, [34, 119, 226, 158, 43, 114]],
|
|
[-Infinity, null],
|
|
[0, null],
|
|
[-0, null],
|
|
]
|
|
|
|
test(`${base64Cases.length} forgiving-base64 inputs decode to the expected bytes or throw a TypeError`, async () => {
|
|
expect(
|
|
await value(`
|
|
const cases = ${JSON.stringify(base64Cases)}
|
|
return cases.flatMap(([input, output]) => {
|
|
try {
|
|
const result = atob(input)
|
|
if (output === null) return [[input, "expected throw"]]
|
|
const bytes = Array.from({ length: result.length }, (_, i) => result.charCodeAt(i))
|
|
return JSON.stringify(bytes) === JSON.stringify(output) ? [] : [[input, bytes]]
|
|
} catch (error) {
|
|
return output === null && error instanceof TypeError ? [] : [[input, error.name]]
|
|
}
|
|
})
|
|
`),
|
|
).toEqual([])
|
|
})
|
|
|
|
test("WebIDL argument conversion stringifies non-string inputs", async () => {
|
|
const literal = (input: unknown) =>
|
|
Object.is(input, -0)
|
|
? "-0"
|
|
: typeof input === "number" || input === undefined
|
|
? String(input)
|
|
: JSON.stringify(input)
|
|
expect(
|
|
await value(`
|
|
const cases = [${idlCases.map(([input, output]) => `[${literal(input)}, ${JSON.stringify(output)}]`).join(",")}]
|
|
return cases.flatMap(([input, output]) => {
|
|
try {
|
|
const result = atob(input)
|
|
if (output === null) return [[String(input), "expected throw"]]
|
|
// The source loop checks only the listed prefix of the decoded bytes.
|
|
const bytes = output.map((_, i) => result.charCodeAt(i))
|
|
return JSON.stringify(bytes) === JSON.stringify(output) ? [] : [[String(input), bytes]]
|
|
} catch (error) {
|
|
return output === null && error instanceof TypeError ? [] : [[String(input), error.name]]
|
|
}
|
|
})
|
|
`),
|
|
).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe("crypto.randomUUID WPT parity (WebCryptoAPI/randomUUID.https.any.js)", () => {
|
|
test("namespace format, version, and variant bits over 256 iterations without collision", async () => {
|
|
expect(
|
|
await value(`
|
|
const uuids = new Set()
|
|
const randomUUID = () => {
|
|
const uuid = crypto.randomUUID()
|
|
if (uuids.has(uuid)) throw new Error("uuid collision " + uuid)
|
|
uuids.add(uuid)
|
|
return uuid
|
|
}
|
|
const UUIDRegex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/
|
|
let format = true, version = true, variant = true
|
|
for (let i = 0; i < 256; i++) format = format && UUIDRegex.test(randomUUID())
|
|
for (let i = 0; i < 256; i++) version = version && (parseInt(randomUUID().split("-")[2].slice(0, 2), 16) & 0b11110000) === 0b01000000
|
|
for (let i = 0; i < 256; i++) variant = variant && (parseInt(randomUUID().split("-")[3].slice(0, 2), 16) & 0b11000000) === 0b10000000
|
|
return [format, version, variant, uuids.size]
|
|
`),
|
|
).toEqual([true, true, true, 768])
|
|
})
|
|
})
|
|
|
|
// Enough of testharness.js to run the Headers files close to verbatim; each `test` records its failure, if any.
|
|
const testharness = `
|
|
const failures = []
|
|
function test(run, name) { try { run() } catch (error) { failures.push(name + ": " + (error && error.message ? error.message : error)) } }
|
|
function assert_equals(actual, expected, message) { if (actual !== expected) throw new Error((message || "") + " expected " + JSON.stringify(expected) + " got " + JSON.stringify(actual)) }
|
|
function assert_true(actual, message) { assert_equals(actual, true, message) }
|
|
function assert_false(actual, message) { assert_equals(actual, false, message) }
|
|
function assert_array_equals(actual, expected, message) { assert_equals(JSON.stringify(actual), JSON.stringify(expected), message) }
|
|
function assert_throws_js(type, run) { try { run() } catch (error) { if (error instanceof type) return; throw new Error("threw " + error.name) } throw new Error("did not throw") }
|
|
function assert_unreached() { throw new Error("unreachable") }
|
|
`
|
|
|
|
describe("Headers WPT parity (fetch/api/headers)", () => {
|
|
test("headers-basic.any.js", async () => {
|
|
expect(
|
|
await value(`
|
|
${testharness}
|
|
test(function() { new Headers() }, "Create headers from no parameter")
|
|
test(function() { new Headers(undefined) }, "Create headers from undefined parameter")
|
|
test(function() { new Headers({}) }, "Create headers from empty object")
|
|
var parameters = [null, 1]
|
|
parameters.forEach(function(parameter) {
|
|
test(function() { assert_throws_js(TypeError, function() { new Headers(parameter) }) }, "Create headers with " + parameter + " should throw")
|
|
})
|
|
var headerDict = {"name1": "value1", "name2": "value2", "name3": "value3", "name4": null, "name5": undefined, "name6": 1, "Content-Type": "value4"}
|
|
var headerSeq = []
|
|
for (var name in headerDict) headerSeq.push([name, headerDict[name]])
|
|
test(function() {
|
|
var headers = new Headers(headerSeq)
|
|
for (name in headerDict) assert_equals(headers.get(name), String(headerDict[name]), "name: " + name + " has value: " + headerDict[name])
|
|
assert_equals(headers.get("length"), null, "init should be treated as a sequence, not as a dictionary")
|
|
}, "Create headers with sequence")
|
|
test(function() {
|
|
var headers = new Headers(headerDict)
|
|
for (name in headerDict) assert_equals(headers.get(name), String(headerDict[name]), "name: " + name + " has value: " + headerDict[name])
|
|
}, "Create headers with record")
|
|
test(function() {
|
|
var headers = new Headers(headerDict)
|
|
var headers2 = new Headers(headers)
|
|
for (name in headerDict) assert_equals(headers2.get(name), String(headerDict[name]), "name: " + name + " has value: " + headerDict[name])
|
|
}, "Create headers with existing headers")
|
|
test(function() {
|
|
var headers = new Headers()
|
|
for (name in headerDict) {
|
|
headers.append(name, headerDict[name])
|
|
assert_equals(headers.get(name), String(headerDict[name]), "name: " + name + " has value: " + headerDict[name])
|
|
}
|
|
}, "Check append method")
|
|
test(function() {
|
|
var headers = new Headers()
|
|
for (name in headerDict) {
|
|
headers.set(name, headerDict[name])
|
|
assert_equals(headers.get(name), String(headerDict[name]), "name: " + name + " has value: " + headerDict[name])
|
|
}
|
|
}, "Check set method")
|
|
test(function() {
|
|
var headers = new Headers(headerDict)
|
|
for (name in headerDict) assert_true(headers.has(name), "headers has name " + name)
|
|
assert_false(headers.has("nameNotInHeaders"), "headers do not have header: nameNotInHeaders")
|
|
}, "Check has method")
|
|
test(function() {
|
|
var headers = new Headers(headerDict)
|
|
for (name in headerDict) {
|
|
assert_true(headers.has(name), "headers have a header: " + name)
|
|
headers.delete(name)
|
|
assert_true(!headers.has(name), "headers do not have anymore a header: " + name)
|
|
}
|
|
}, "Check delete method")
|
|
test(function() {
|
|
var headers = new Headers(headerDict)
|
|
for (name in headerDict) assert_equals(headers.get(name), String(headerDict[name]), "name: " + name + " has value: " + headerDict[name])
|
|
assert_equals(headers.get("nameNotInHeaders"), null, "header: nameNotInHeaders has no value")
|
|
}, "Check get method")
|
|
var headerEntriesDict = {"name1": "value1", "Name2": "value2", "name": "value3", "content-Type": "value4", "Content-Typ": "value5", "Content-Types": "value6"}
|
|
var sortedHeaderDict = {}
|
|
var headerValues = []
|
|
var sortedHeaderKeys = Object.keys(headerEntriesDict).map(function(value) {
|
|
sortedHeaderDict[value.toLowerCase()] = headerEntriesDict[value]
|
|
headerValues.push(headerEntriesDict[value])
|
|
return value.toLowerCase()
|
|
}).sort()
|
|
test(function() {
|
|
var headers = new Headers(headerEntriesDict)
|
|
var actual = headers.keys()
|
|
sortedHeaderKeys.forEach(function(key) {
|
|
const entry = actual.next()
|
|
assert_false(entry.done)
|
|
assert_equals(entry.value, key)
|
|
})
|
|
assert_true(actual.next().done)
|
|
assert_true(actual.next().done)
|
|
for (const key of headers.keys()) assert_true(sortedHeaderKeys.indexOf(key) != -1)
|
|
}, "Check keys method")
|
|
test(function() {
|
|
var headers = new Headers(headerEntriesDict)
|
|
var actual = headers.values()
|
|
sortedHeaderKeys.forEach(function(key) {
|
|
const entry = actual.next()
|
|
assert_false(entry.done)
|
|
assert_equals(entry.value, sortedHeaderDict[key])
|
|
})
|
|
assert_true(actual.next().done)
|
|
assert_true(actual.next().done)
|
|
for (const value of headers.values()) assert_true(headerValues.indexOf(value) != -1)
|
|
}, "Check values method")
|
|
test(function() {
|
|
var headers = new Headers(headerEntriesDict)
|
|
var actual = headers.entries()
|
|
sortedHeaderKeys.forEach(function(key) {
|
|
const entry = actual.next()
|
|
assert_false(entry.done)
|
|
assert_equals(entry.value[0], key)
|
|
assert_equals(entry.value[1], sortedHeaderDict[key])
|
|
})
|
|
assert_true(actual.next().done)
|
|
assert_true(actual.next().done)
|
|
for (const entry of headers.entries()) assert_equals(entry[1], sortedHeaderDict[entry[0]])
|
|
}, "Check entries method")
|
|
test(function() {
|
|
var headers = new Headers(headerEntriesDict)
|
|
var actual = headers[Symbol.iterator]()
|
|
sortedHeaderKeys.forEach(function(key) {
|
|
const entry = actual.next()
|
|
assert_false(entry.done)
|
|
assert_equals(entry.value[0], key)
|
|
assert_equals(entry.value[1], sortedHeaderDict[key])
|
|
})
|
|
assert_true(actual.next().done)
|
|
assert_true(actual.next().done)
|
|
}, "Check Symbol.iterator method")
|
|
test(function() {
|
|
var headers = new Headers(headerEntriesDict)
|
|
var reference = sortedHeaderKeys[Symbol.iterator]()
|
|
headers.forEach(function(value, key, container) {
|
|
assert_equals(headers, container)
|
|
const entry = reference.next()
|
|
assert_false(entry.done)
|
|
assert_equals(key, entry.value)
|
|
assert_equals(value, sortedHeaderDict[entry.value])
|
|
})
|
|
assert_true(reference.next().done)
|
|
}, "Check forEach method")
|
|
test(() => {
|
|
const headers = new Headers({"foo": "2", "baz": "1", "BAR": "0"})
|
|
const actualKeys = []
|
|
const actualValues = []
|
|
for (const [header, value] of headers) {
|
|
actualKeys.push(header)
|
|
actualValues.push(value)
|
|
headers.delete("foo")
|
|
}
|
|
assert_array_equals(actualKeys, ["bar", "baz"])
|
|
assert_array_equals(actualValues, ["0", "1"])
|
|
}, "Iteration skips elements removed while iterating")
|
|
test(() => {
|
|
const headers = new Headers({"foo": "2", "baz": "1", "BAR": "0", "quux": "3"})
|
|
const actualKeys = []
|
|
const actualValues = []
|
|
for (const [header, value] of headers) {
|
|
actualKeys.push(header)
|
|
actualValues.push(value)
|
|
if (header === "baz") headers.delete("bar")
|
|
}
|
|
assert_array_equals(actualKeys, ["bar", "baz", "quux"])
|
|
assert_array_equals(actualValues, ["0", "1", "3"])
|
|
}, "Removing elements already iterated over causes an element to be skipped during iteration")
|
|
test(() => {
|
|
const headers = new Headers({"foo": "2", "baz": "1", "BAR": "0", "quux": "3"})
|
|
const actualKeys = []
|
|
const actualValues = []
|
|
for (const [header, value] of headers) {
|
|
actualKeys.push(header)
|
|
actualValues.push(value)
|
|
if (header === "baz") headers.append("X-yZ", "4")
|
|
}
|
|
assert_array_equals(actualKeys, ["bar", "baz", "foo", "quux", "x-yz"])
|
|
assert_array_equals(actualValues, ["0", "1", "2", "3", "4"])
|
|
}, "Appending a value pair during iteration causes it to be reached during iteration")
|
|
test(() => {
|
|
const headers = new Headers({"foo": "2", "baz": "1", "BAR": "0", "quux": "3"})
|
|
const actualKeys = []
|
|
const actualValues = []
|
|
for (const [header, value] of headers) {
|
|
actualKeys.push(header)
|
|
actualValues.push(value)
|
|
if (header === "baz") headers.append("abc", "-1")
|
|
}
|
|
assert_array_equals(actualKeys, ["bar", "baz", "baz", "foo", "quux"])
|
|
assert_array_equals(actualValues, ["0", "1", "1", "2", "3"])
|
|
}, "Prepending a value pair before the current element position causes it to be skipped during iteration and adds the current element a second time")
|
|
return failures
|
|
`),
|
|
).toEqual([])
|
|
})
|
|
|
|
test("headers-errors.any.js", async () => {
|
|
expect(
|
|
await value(`
|
|
${testharness}
|
|
test(function() { assert_throws_js(TypeError, function() { new Headers([["name"]]) }) }, "Create headers giving an array having one string as init argument")
|
|
test(function() { assert_throws_js(TypeError, function() { new Headers([["invalid", "invalidValue1", "invalidValue2"]]) }) }, "Create headers giving an array having three strings as init argument")
|
|
test(function() { assert_throws_js(TypeError, function() { new Headers([["invalid\u0100", "Value1"]]) }) }, "Create headers giving bad header name as init argument")
|
|
test(function() { assert_throws_js(TypeError, function() { new Headers([["name", "invalidValue\u0100"]]) }) }, "Create headers giving bad header value as init argument")
|
|
var badNames = ["invalid\u0100", {}]
|
|
var badValues = ["invalid\u0100"]
|
|
badNames.forEach(function(name) {
|
|
test(function() { var headers = new Headers(); assert_throws_js(TypeError, function() { headers.get(name) }) }, "Check headers get with an invalid name " + name)
|
|
})
|
|
badNames.forEach(function(name) {
|
|
test(function() { var headers = new Headers(); assert_throws_js(TypeError, function() { headers.delete(name) }) }, "Check headers delete with an invalid name " + name)
|
|
})
|
|
badNames.forEach(function(name) {
|
|
test(function() { var headers = new Headers(); assert_throws_js(TypeError, function() { headers.has(name) }) }, "Check headers has with an invalid name " + name)
|
|
})
|
|
badNames.forEach(function(name) {
|
|
test(function() { var headers = new Headers(); assert_throws_js(TypeError, function() { headers.set(name, "Value1") }) }, "Check headers set with an invalid name " + name)
|
|
})
|
|
badValues.forEach(function(value) {
|
|
test(function() { var headers = new Headers(); assert_throws_js(TypeError, function() { headers.set("name", value) }) }, "Check headers set with an invalid value " + value)
|
|
})
|
|
badNames.forEach(function(name) {
|
|
test(function() { var headers = new Headers(); assert_throws_js(TypeError, function() { headers.append("invalid\u0100", "Value1") }) }, "Check headers append with an invalid name " + name)
|
|
})
|
|
badValues.forEach(function(value) {
|
|
test(function() { var headers = new Headers(); assert_throws_js(TypeError, function() { headers.append("name", value) }) }, "Check headers append with an invalid value " + value)
|
|
})
|
|
test(function() {
|
|
var headers = new Headers([["name", "value"]])
|
|
assert_throws_js(TypeError, function() { headers.forEach() })
|
|
assert_throws_js(TypeError, function() { headers.forEach(undefined) })
|
|
assert_throws_js(TypeError, function() { headers.forEach(1) })
|
|
}, "Headers forEach throws if argument is not callable")
|
|
test(function() {
|
|
var headers = new Headers([["name1", "value1"], ["name2", "value2"], ["name3", "value3"]])
|
|
var counter = 0
|
|
try {
|
|
headers.forEach(function(value, name) {
|
|
counter++
|
|
if (name == "name2") throw "error"
|
|
})
|
|
} catch (e) {
|
|
assert_equals(counter, 2)
|
|
assert_equals(e, "error")
|
|
return
|
|
}
|
|
assert_unreached()
|
|
}, "Headers forEach loop should stop if callback is throwing exception")
|
|
return failures
|
|
`),
|
|
).toEqual([])
|
|
})
|
|
})
|