Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
do_get_profile();
const { HttpServer } = ChromeUtils.importESModule(
);
const {
worldCupMatches,
worldCupLive,
toolsConfig,
TOOLS,
WORLD_CUP_MATCHES,
WORLD_CUP_LIVE,
WORLD_CUP_PREF,
} = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/models/Tools.sys.mjs"
);
const ENDPOINT_PREF = "browser.smartwindow.worldcup.endpointURL";
const TIMEOUT_PREF = "browser.smartwindow.worldcup.timeoutMs";
function fakeMatch(home, away, score) {
return {
date: "2026-06-15",
global_event_id: 12345,
home_team: {
key: home,
global_team_id: 1,
name: home,
region: "South America",
colors: ["#009C3B"],
eliminated: false,
},
away_team: {
key: away,
global_team_id: 2,
name: away,
region: "South America",
colors: ["#FCD116"],
eliminated: false,
},
period: "FT",
home_score: score[0],
away_score: score[1],
home_extra: 0,
away_extra: 0,
home_penalty: 0,
away_penalty: 0,
clock: "90:00",
updated: 1718481600,
status: "Final",
status_type: "final",
query: `${home} vs ${away}`,
sport: "soccer",
};
}
let server;
let lastRequest;
// Test-controlled response mode for /matches: "ok" or "500".
let matchesMode = "ok";
add_setup(async function () {
server = new HttpServer();
server.registerPathHandler("/api/v1/wcs/matches", (request, response) => {
lastRequest = { path: request.path, query: request.queryString };
if (matchesMode === "500") {
response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
response.write("kaboom");
return;
}
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "application/json", false);
response.write(
JSON.stringify({
previous: [fakeMatch("BRA", "COL", [3, 1])],
current: [fakeMatch("ARG", "ALG", [3, 0])],
next: [fakeMatch("MEX", "AUS", [1, 2])],
})
);
});
server.registerPathHandler("/api/v1/wcs/live", (request, response) => {
lastRequest = { path: request.path, query: request.queryString };
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "application/json", false);
response.write(
JSON.stringify({
matches: [{ ...fakeMatch("BRA", "ARG", [1, 1]), status_type: "live" }],
})
);
});
server.start(-1);
const port = server.identity.primaryPort;
Services.prefs.setStringPref(ENDPOINT_PREF, `http://localhost:${port}`);
Services.prefs.setIntPref(TIMEOUT_PREF, 5000);
registerCleanupFunction(async () => {
Services.prefs.clearUserPref(ENDPOINT_PREF);
Services.prefs.clearUserPref(TIMEOUT_PREF);
Services.prefs.clearUserPref(WORLD_CUP_PREF);
await new Promise(r => server.stop(r));
});
});
add_task(async function test_world_cup_tools_registered() {
Assert.ok(
TOOLS.includes(WORLD_CUP_MATCHES),
"TOOLS array contains world_cup_matches"
);
Assert.ok(
TOOLS.includes(WORLD_CUP_LIVE),
"TOOLS array contains world_cup_live"
);
const names = toolsConfig.map(t => t.function.name);
Assert.ok(names.includes(WORLD_CUP_MATCHES), "schema present");
Assert.ok(names.includes(WORLD_CUP_LIVE), "schema present");
});
add_task(async function test_worldCupMatches_shape_and_trimming() {
matchesMode = "ok";
const conversation = makeConversation();
const result = await worldCupMatches(
{ date: "2026-06-15", teams: "BRA,ARG", limit: 10 },
conversation
);
Assert.ok(Array.isArray(result.previous), "previous bucket present");
Assert.ok(Array.isArray(result.current), "current bucket present");
Assert.ok(Array.isArray(result.next), "next bucket present");
Assert.equal(result.current.length, 1, "one current match");
const team = result.current[0].home_team;
Assert.equal(team.key, "ARG", "team key preserved");
Assert.equal(team.name, "ARG", "team name preserved");
Assert.ok(!("icon_url" in team), "icon_url stripped from team");
Assert.ok(!("colors" in team), "colors stripped from team");
Assert.equal(typeof result.current[0].home_score, "number", "score kept");
Assert.ok(
lastRequest.query.includes("date=2026-06-15"),
`date query param forwarded: ${lastRequest.query}`
);
Assert.ok(
lastRequest.query.includes("teams=BRA%2CARG"),
`teams query param forwarded: ${lastRequest.query}`
);
Assert.ok(
lastRequest.query.includes("limit=10"),
`limit forwarded: ${lastRequest.query}`
);
conversation.securityProperties.commit();
Assert.equal(
conversation.securityProperties.untrustedInput,
true,
"untrusted_input set"
);
});
add_task(async function test_worldCupMatches_omits_empty_params() {
matchesMode = "ok";
lastRequest = null;
const conversation = makeConversation();
await worldCupMatches({}, conversation);
Assert.equal(lastRequest.query, "", "no query params when none provided");
});
add_task(async function test_worldCupLive_shape() {
const conversation = makeConversation();
const result = await worldCupLive({ teams: "BRA" }, conversation);
Assert.ok(Array.isArray(result.matches), "matches array present");
Assert.equal(result.matches.length, 1, "one live match");
Assert.ok(!("icon_url" in result.matches[0].home_team), "icon_url stripped");
});
add_task(async function test_worldCupMatches_returns_error_on_http_failure() {
matchesMode = "500";
const conversation = makeConversation();
const result = await worldCupMatches({}, conversation);
Assert.ok(result.error, "returns error object on HTTP 500");
Assert.ok(
result.error.includes("HTTP 500"),
`error message mentions status: ${result.error}`
);
matchesMode = "ok";
});
add_task(async function test_worldCupMatches_returns_error_on_unreachable() {
Services.prefs.setStringPref(ENDPOINT_PREF, "http://localhost:1"); // refused
Services.prefs.setIntPref(TIMEOUT_PREF, 250);
const conversation = makeConversation();
const result = await worldCupMatches({}, conversation);
Assert.ok(result.error, "returns error object on connection failure");
Services.prefs.setStringPref(
ENDPOINT_PREF,
`http://localhost:${server.identity.primaryPort}`
);
Services.prefs.setIntPref(TIMEOUT_PREF, 5000);
});