Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125// Checks the OAuth 1.0a signing in toolchain/x/x.mjs against the worked// example X publishes in "Authorizing a request", and checks the weighted// character count that decides whether a post fits.//// This matters because the signing code cannot be exercised against the live// API until a developer app exists (toolchain/x/SETUP.md). A wrong signature// fails as a bare 401, which reads exactly like a bad key.
import assert from "node:assert/strict";import { dirname, resolve } from "node:path";import { fileURLToPath } from "node:url";import test from "node:test";
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");const { authorize, explain, parsePostId, searchBudgetPlan, weigh } = await import( resolve(ROOT, "toolchain/x/x.mjs"),);
// https://developer.x.com/en/docs/authentication/oauth-1-0a/creating-a-signatureconst VECTOR = { method: "POST", url: "https://api.twitter.com/1.1/statuses/update.json", params: { status: "Hello Ladies + Gentlemen, a signed OAuth request!", include_entities: "true", }, creds: { apiKey: "xvz1evFS4wEEPTGEFPHBog", apiSecret: "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw", accessToken: "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", accessSecret: "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE", }, fixed: { nonce: "kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", timestamp: "1318622958", }, signature: "hCtSmYh+iHYCEqBWrE7C7hYmtUk=",};
test("signs X's published example exactly", () => { const header = authorize(VECTOR.method, VECTOR.url, VECTOR.params, VECTOR.creds, VECTOR.fixed); const signature = decodeURIComponent( header.match(/oauth_signature="([^"]+)"/)[1], ); assert.equal(signature, VECTOR.signature);});
test("header carries every required oauth field", () => { const header = authorize("GET", "https://api.x.com/2/users/me", {}, VECTOR.creds, VECTOR.fixed); for (const field of ["oauth_consumer_key", "oauth_nonce", "oauth_signature", "oauth_signature_method", "oauth_timestamp", "oauth_token", "oauth_version"]) { assert.ok(header.includes(`${field}="`), `missing ${field}`); }});
test("a fresh call does not reuse a nonce", () => { const nonce = (header) => header.match(/oauth_nonce="([^"]+)"/)[1]; const a = authorize("GET", "https://api.x.com/2/users/me", {}, VECTOR.creds); const b = authorize("GET", "https://api.x.com/2/users/me", {}, VECTOR.creds); assert.notEqual(nonce(a), nonce(b));});
test("a URL weighs 23 no matter how long it is", () => { const short = weigh("a https://x.co"); const long = weigh("a https://www.culturehub.org/events/grokaesthetic-workshop"); assert.equal(short, long); assert.equal(long, 2 + 23);});
test("plain text weighs its own length", () => { assert.equal(weigh("hello"), 5);});
test("a post that only overflows on raw length is allowed through", () => { const url = "https://www.culturehub.org/events/grokaesthetic-workshop"; const text = "x".repeat(250) + " " + url; assert.ok(text.length > 280, "raw length should exceed the limit"); assert.ok(weigh(text) <= 280, "weighted length should fit");});
test("reply targets accept ids and canonical status URLs", () => { assert.equal(parsePostId("2095871975876972564"), "2095871975876972564"); assert.equal( parsePostId("https://x.com/justinaversano/status/2095871975876972564?s=20"), "2095871975876972564", ); assert.equal( parsePostId("https://twitter.com/justinaversano/status/2095871975876972564"), "2095871975876972564", ); assert.throws(() => parsePostId("https://example.com/post/1"), /X post id/);});
test("search budget reserves the request's worst-case read cost", () => { const plan = searchBudgetPlan({ spentUsd: 0.05, maxResults: 10, rateUsd: 0.005, budgetUsd: 0.25, }); assert.equal(plan.estimatedUsd, 0.05); assert.ok(Math.abs(plan.remainingUsd - 0.15) < Number.EPSILON * 2);});
test("search budget blocks before a request can cross the daily limit", () => { assert.throws(() => searchBudgetPlan({ spentUsd: 0.21, maxResults: 10, rateUsd: 0.005, budgetUsd: 0.25, }), /search blocked/);});
test("explains the self-serve reply restriction instead of blaming credentials", () => { const message = explain(403, JSON.stringify({ detail: "You can only reply to or quote posts where you are mentioned or are the author.", })); assert.match(message, /self-serve API accounts/); assert.match(message, /manually in X/); assert.doesNotMatch(message, /lacks Write permission/);});