Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test runs only with pattern: os != 'android'
- Manifest: browser/components/aiwindow/ui/test/xpcshell/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
// Deleting a generated page has to clear two databases: the page versions in
// ai-tab-pages-store.sqlite and the conversation that produced them in
// conversation-store.sqlite. They are separate files, so no foreign key
// cascades between them and the two deletes cannot share a transaction. These
// tests pin the contract AITabParent relies on.
do_get_profile();
const { AITabStore } = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/ui/modules/AITabStore.sys.mjs"
);
const { ConversationStore } = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/ui/modules/ConversationStore.sys.mjs"
);
const { Conversation } = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/models/Conversation.sys.mjs"
);
registerCleanupFunction(async () => {
await AITabStore.destroyDatabase();
await ConversationStore.destroyDatabase();
});
/**
* Creates a conversation and a two-version page belonging to it.
*
* @param {string} convId
* @param {string} slug
*/
async function createTabWithConversation(convId, slug) {
await ConversationStore.updateConversation(
new Conversation({ id: convId, feature: "aitab" })
);
await AITabStore.create({ convId, slug, title: "V1" });
await AITabStore.edit({ convId, slug, title: "V2" });
}
add_task(async function setup() {
await AITabStore.ensureDatabase();
await ConversationStore.ensureDatabase();
});
add_task(async function test_created_at_is_microseconds() {
await createTabWithConversation("conv-units", "units-slug");
const page = await AITabStore.getBySlug("units-slug");
// AITabStore writes `Date.now() * 1000`. The header divides this back down
// before building a Date, so if the column ever switches to milliseconds
// that conversion has to go with it.
Assert.greater(
page.createdAt,
Date.now() * 100,
"created_at is stored in microseconds, not milliseconds"
);
Assert.equal(
new Date(Math.round(page.createdAt / 1000)).getFullYear(),
new Date().getFullYear(),
"Dividing by 1000 yields a Date in the current year"
);
});
add_task(async function test_delete_clears_both_stores() {
await createTabWithConversation("conv-both", "both-slug");
// Sanity: both sides exist before the delete, so the assertions below are
// observing a real transition rather than an empty database.
Assert.ok(
await AITabStore.getBySlug("both-slug"),
"The page exists before deleting"
);
Assert.ok(
await ConversationStore.findConversationById("conv-both"),
"The conversation exists before deleting"
);
// The order AITabParent uses: pages first, conversation second. The page
// delete is keyed on slug, the conversation delete on conv_id.
await AITabStore.deleteBySlug("both-slug");
await ConversationStore.deleteConversationById("conv-both");
Assert.equal(
await AITabStore.getBySlug("both-slug"),
null,
"The page no longer resolves by slug"
);
Assert.equal(
await ConversationStore.findConversationById("conv-both"),
null,
"The conversation is gone too"
);
});
add_task(async function test_deleting_conversation_alone_orphans_the_page() {
await createTabWithConversation("conv-orphan", "orphan-slug");
await ConversationStore.deleteConversationById("conv-orphan");
// This is why AITabParent cannot rely on the conversation delete alone: the
// stores are different database files, so nothing cascades and the page
// would still load by slug.
Assert.equal(
await ConversationStore.findConversationById("conv-orphan"),
null,
"The conversation is gone"
);
Assert.ok(
await AITabStore.getBySlug("orphan-slug"),
"The page survives, so deleting the conversation alone is not enough"
);
});
add_task(async function test_delete_is_scoped_to_one_conversation() {
await createTabWithConversation("conv-a", "slug-a");
await createTabWithConversation("conv-b", "slug-b");
await AITabStore.deleteBySlug("slug-a");
await ConversationStore.deleteConversationById("conv-a");
Assert.equal(
await AITabStore.getBySlug("slug-a"),
null,
"The targeted page is gone"
);
Assert.ok(
await AITabStore.getBySlug("slug-b"),
"The other conversation's page is untouched"
);
Assert.ok(
await ConversationStore.findConversationById("conv-b"),
"The other conversation is untouched"
);
});