Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/* Any copyright is dedicated to the Public Domain.
"use strict";
const { ObliviousHTTP } = ChromeUtils.importESModule(
"resource://gre/modules/ObliviousHTTP.sys.mjs"
);
const { sinon } = ChromeUtils.importESModule(
);
const { HttpServer } = ChromeUtils.importESModule(
);
const { TestUtils } = ChromeUtils.importESModule(
);
let gHttpServer;
/**
* Waits for an nsICacheEntry to exist for urlString in the anonymous load
* context with > 0 data size.
*
* @param {string} urlString
* The string of the URL to check for.
* @returns {Promise<undefined>}
*/
async function waitForCacheEntry(urlString) {
const lci = Services.loadContextInfo.anonymous;
const storage = Services.cache2.diskCacheStorage(lci);
const uri = Services.io.newURI(urlString);
await TestUtils.waitForCondition(() => {
try {
return storage.exists(uri, "");
} catch (e) {
return false;
}
});
let entry = await new Promise((resolve, reject) => {
storage.asyncOpenURI(uri, "", Ci.nsICacheStorage.OPEN_READONLY, {
onCacheEntryCheck: () => Ci.nsICacheEntryOpenCallback.ENTRY_WANTED,
onCacheEntryAvailable: (foundEntry, isNew, status) => {
if (Components.isSuccessCode(status)) {
resolve(foundEntry);
} else {
reject(new Error(`Cache entry operation failed: ${status}`));
}
},
});
});
await TestUtils.waitForCondition(() => {
try {
return entry.dataSize > 0;
} catch (e) {
return false;
}
});
}
add_setup(async function () {
// Start HTTP server for cache testing
gHttpServer = new HttpServer();
gHttpServer.registerPathHandler(
"/cached-image.jpg",
function (request, response) {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "image/jpeg");
response.setHeader("Cache-Control", "max-age=3600");
response.write("fake-image-data");
}
);
gHttpServer.start(-1);
// Set OHTTP preferences for testing
Services.prefs.setCharPref(
"browser.newtabpage.activity-stream.discoverystream.ohttp.configURL",
);
Services.prefs.setCharPref(
"browser.newtabpage.activity-stream.discoverystream.ohttp.relayURL",
);
registerCleanupFunction(async () => {
await new Promise(resolve => gHttpServer.stop(resolve));
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.discoverystream.ohttp.configURL"
);
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.discoverystream.ohttp.relayURL"
);
});
});
/**
* Test that OHTTP responses populate the cache and subsequent requests use cache.
*/
add_task(async function test_ohttp_populates_cache_and_cache_hit() {
const sandbox = sinon.createSandbox();
try {
MockOHTTPService.reset();
// Stub ObliviousHTTP.getOHTTPConfig to avoid network requests
sandbox
.stub(ObliviousHTTP, "getOHTTPConfig")
.resolves(new Uint8Array([1, 2, 3, 4]));
const testURI = createTestOHTTPResourceURI(imageURL);
// First request - should go via OHTTP and populate cache
const firstChannel = createTestChannel(testURI);
let firstRequestData = "";
await new Promise(resolve => {
const listener = createDataCollectingListener((data, success) => {
Assert.ok(success, "First request should succeed");
firstRequestData = data;
resolve();
});
firstChannel.asyncOpen(listener);
});
// Verify OHTTP service was called for first request
Assert.ok(
MockOHTTPService.channelCreated,
"Should call OHTTP service for cache miss"
);
Assert.equal(
MockOHTTPService.totalChannels,
1,
"Should make one OHTTP request"
);
Assert.greater(
firstRequestData.length,
0,
"Should receive data from OHTTP"
);
// Reset mock service state for second request
MockOHTTPService.channelCreated = false;
await waitForCacheEntry(imageURL);
Assert.ok(true, "Found cache entry.");
// Second request - should hit cache without calling OHTTP
const secondChannel = createTestChannel(testURI);
let secondRequestData = "";
let cacheHit = false;
await new Promise(resolve => {
const listener = createDataCollectingListener((data, success) => {
Assert.ok(success, "Second request should succeed");
secondRequestData = data;
cacheHit = true;
resolve();
});
secondChannel.asyncOpen(listener);
});
// Verify cache hit behavior
Assert.ok(cacheHit, "Second request should succeed from cache");
Assert.ok(
!MockOHTTPService.channelCreated,
"Should not call OHTTP service for cache hit"
);
Assert.equal(
MockOHTTPService.totalChannels,
1,
"Should still be only one OHTTP request"
);
Assert.equal(
firstRequestData,
secondRequestData,
"Cache hit should return same data as original request"
);
} finally {
sandbox.restore();
}
});
/**
* Test OHTTP fallback when cache miss occurs.
*/
add_task(async function test_ohttp_fallback_on_cache_miss() {
const sandbox = sinon.createSandbox();
try {
// Reset mock service state
MockOHTTPService.reset();
// Stub ObliviousHTTP.getOHTTPConfig to avoid network requests
sandbox
.stub(ObliviousHTTP, "getOHTTPConfig")
.resolves(new Uint8Array([1, 2, 3, 4]));
// Use a URL that won't be in cache
const uncachedImageURL = `https://localhost:${gHttpServer.identity.primaryPort}/uncached-image.jpg`;
const testURI = createTestOHTTPResourceURI(uncachedImageURL);
const channel = createTestChannel(testURI);
let loadCompleted = false;
let receivedData = false;
await new Promise(resolve => {
const listener = createCompletionListener((success, hasData) => {
loadCompleted = success;
receivedData = hasData;
resolve();
});
channel.asyncOpen(listener);
});
// Verify that the mock OHTTP service was called
Assert.ok(
MockOHTTPService.channelCreated,
"Should call OHTTP service when cache miss occurs"
);
Assert.ok(loadCompleted, "Should load successfully via mocked OHTTP");
Assert.ok(receivedData, "Should receive data via mocked OHTTP");
// Verify the correct URLs were passed to the OHTTP service
Assert.equal(
MockOHTTPService.lastTargetURI.spec,
uncachedImageURL,
"Should request correct target URL via OHTTP"
);
Assert.equal(
MockOHTTPService.lastRelayURI.spec,
"Should use correct relay URL"
);
} finally {
sandbox.restore();
}
});