import assert from "node:assert/strict"; import { mkdir, writeFile } from "node:fs/promises"; import path from "node:path"; import test from "node:test"; import { withApp } from "../lib/app.mjs"; import { extensionZipBase64, wireGuardFixture } from "../lib/fixtures.mjs"; async function createProfile(app, name = "Entity Profile") { return app.invoke("create_browser_profile_new", { name, browserStr: "wayfern", version: "150.0.7871.100", releaseType: "stable", proxyId: null, vpnId: null, // CRUD-focused suites use a deterministic stored fingerprint. The browser // suite separately exercises real Wayfern fingerprint generation. wayfernConfig: { fingerprint: "{}" }, groupId: null, ephemeral: false, dnsBlocklist: null, launchHook: null, }); } test("profile, group, proxy, tag, metadata, clone, and bulk-delete lifecycle", async () => { await withApp("entities-core", async (app) => { const group = await app.invoke("create_profile_group", { name: "Research", }); assert.equal(group.name, "Research"); const renamedGroup = await app.invoke("update_profile_group", { groupId: group.id, name: "Research Team", }); assert.equal(renamedGroup.name, "Research Team"); const duplicateError = await app.invokeError("create_profile_group", { name: "Research Team", }); assert.match(duplicateError, /GROUP_ALREADY_EXISTS|already exists/i); const proxy = await app.invoke("create_stored_proxy", { name: "Local Dead Proxy", proxySettings: { proxy_type: "http", host: "127.0.0.1", port: 9, username: "e2e-user", password: "e2e-pass", }, }); assert.equal(proxy.proxy_settings.password, "e2e-pass"); const updatedProxy = await app.invoke("update_stored_proxy", { proxyId: proxy.id, name: "Updated Proxy", proxySettings: { proxy_type: "socks5", host: "127.0.0.1", port: 9, username: null, password: null, }, }); assert.equal(updatedProxy.name, "Updated Proxy"); assert.equal(updatedProxy.updated_at >= proxy.updated_at, true); const parsed = await app.invoke("parse_txt_proxies", { content: [ "http://one.example:8080", "two.example:1080:user:pass", "not a proxy", ].join("\n"), }); assert.equal(parsed.length, 3); assert.ok(parsed.some((result) => result.status === "parsed")); assert.ok(parsed.some((result) => result.status === "invalid")); const parsedProxy = parsed.find((result) => result.status === "parsed"); const { status: _status, ...parsedProxyFields } = parsedProxy; const parsedImport = await app.invoke("import_proxies_from_parsed", { parsedProxies: [parsedProxyFields], namePrefix: "Parsed", }); assert.equal(parsedImport.imported_count, 1); const validityError = await app.invokeError("check_proxy_validity", { proxyId: proxy.id, proxySettings: null, }); assert.match(validityError, /Proxy check failed|Could not connect/i); const cachedValidity = await app.invoke("get_cached_proxy_check", { proxyId: proxy.id, }); assert.ok(cachedValidity === null || cachedValidity.is_valid === false); const exported = JSON.parse( await app.invoke("export_proxies", { format: "json" }), ); assert.equal(exported.proxies.length, 2); assert.ok(exported.proxies.some((item) => item.name === "Updated Proxy")); assert.ok(exported.proxies.some((item) => item.name === "Parsed Proxy 1")); const importResult = await app.invoke("import_proxies_json", { content: JSON.stringify({ version: "1", source: "Donut Browser", exported_at: new Date().toISOString(), proxies: [ { name: "Imported Proxy", type: "http", host: "127.0.0.1", port: 8081, }, ], }), }); assert.equal(importResult.imported_count, 1); const profile = await createProfile(app); assert.equal(profile.name, "Entity Profile"); assert.equal( ( await app.invoke("update_profile_proxy", { profileId: profile.id, proxyId: proxy.id, }) ).proxy_id, proxy.id, ); await app.invoke("assign_profiles_to_group", { profileIds: [profile.id], groupId: group.id, }); await app.invoke("rename_profile", { profileId: profile.id, newName: "Renamed Profile", }); await app.invoke("update_profile_tags", { profileId: profile.id, tags: ["alpha", "automation"], }); await app.invoke("update_profile_note", { profileId: profile.id, note: "Extensive E2E metadata", }); await app.invoke("update_profile_window_color", { profileId: profile.id, windowColor: "#123456", }); await app.invoke("update_profile_launch_hook", { profileId: profile.id, launchHook: `${process.env.DONUT_E2E_FIXTURE_URL}/launch-hook`, }); const invalidHook = await app.invokeError("update_profile_launch_hook", { profileId: profile.id, launchHook: "file:///etc/passwd", }); assert.match(invalidHook, /INVALID_LAUNCH_HOOK_URL/); await app.invoke("update_profile_proxy_bypass_rules", { profileId: profile.id, rules: ["localhost", "*.internal.example"], }); await app.invoke("update_profile_dns_blocklist", { profileId: profile.id, dnsBlocklist: "light", }); await app.invoke("update_profile_clear_on_close", { profileId: profile.id, clearOnClose: true, }); const profiles = await app.invoke("list_browser_profiles"); const changed = profiles.find((item) => item.id === profile.id); assert.deepEqual(changed.tags, ["alpha", "automation"]); assert.equal(changed.note, "Extensive E2E metadata"); assert.equal(changed.window_color, "#123456"); assert.equal(changed.group_id, group.id); assert.deepEqual(changed.proxy_bypass_rules, [ "localhost", "*.internal.example", ]); assert.equal(changed.dns_blocklist, "light"); assert.equal(changed.clear_on_close, true); assert.deepEqual((await app.invoke("get_all_tags")).sort(), [ "alpha", "automation", ]); assert.ok(Array.isArray(await app.invoke("detect_existing_profiles"))); const importRoot = path.join(app.root, "profile-import-fixture"); const importProfile = path.join(importRoot, "Default"); await mkdir(importProfile, { recursive: true }); await writeFile( path.join(importProfile, "Preferences"), JSON.stringify({ profile: { name: "Imported fixture" } }), ); const scanned = await app.invoke("scan_folder_for_profiles", { folderPath: importRoot, }); assert.equal(scanned.length, 1); assert.equal(scanned[0].mapped_browser, "wayfern"); const importBatch = await app.invoke("import_browser_profiles", { items: [ { source_path: scanned[0].path, browser_type: scanned[0].browser, new_profile_name: "Imported Profile", proxy_id: null, vpn_id: null, }, ], groupId: null, duplicateStrategy: "rename", wayfernConfig: null, }); assert.equal(importBatch.imported_count + importBatch.failed_count, 1); const archivePath = path.join(app.root, "profile-import-fixture.zip"); await writeFile(archivePath, Buffer.from(extensionZipBase64(), "base64")); const archiveScan = await app.invoke("scan_profile_archive", { archivePath, }); assert.ok(Array.isArray(archiveScan.profiles)); await app.invoke("cleanup_profile_import_scratch", { extractedDir: archiveScan.extracted_dir, }); const clone = await app.invoke("clone_profile", { profileId: profile.id, name: "Cloned Profile", }); assert.notEqual(clone.id, profile.id); assert.equal(clone.name, "Cloned Profile"); const counts = await app.invoke("get_groups_with_profile_counts"); assert.equal(counts.find((item) => item.id === group.id).count, 2); assert.equal((await app.invoke("get_profile_groups")).length, 1); await app.invoke("delete_selected_profiles", { profileIds: [profile.id, clone.id], }); assert.deepEqual(await app.invoke("list_browser_profiles"), []); await app.invoke("delete_profile_group", { groupId: group.id }); await app.invoke("delete_stored_proxy", { proxyId: proxy.id }); for (const importedProxy of (await app.invoke("get_stored_proxies")).filter( (item) => item.name === "Imported Proxy" || item.name.startsWith("Parsed Proxy"), )) { await app.invoke("delete_stored_proxy", { proxyId: importedProxy.id }); } }); }); test("extensions, extension groups, VPN storage, DNS rules, and event-backed assignments", async () => { await withApp("entities-network-extension", async (app) => { const profile = await createProfile(app, "Assignment Profile"); const extension = await app.invoke("add_extension", { name: "E2E Fixture Extension", fileName: "fixture.zip", fileData: [...Buffer.from(extensionZipBase64(), "base64")], }); assert.equal(extension.name, "Donut E2E Fixture"); assert.equal(extension.version, "1.0.0"); const extensionGroup = await app.invoke("create_extension_group", { name: "Automation Extensions", }); const populated = await app.invoke("add_extension_to_group", { groupId: extensionGroup.id, extensionId: extension.id, }); assert.deepEqual(populated.extension_ids, [extension.id]); await app.invoke("assign_extension_group_to_profile", { profileId: profile.id, extensionGroupId: extensionGroup.id, }); assert.equal( ( await app.invoke("get_extension_group_for_profile", { profileId: profile.id, }) ).id, extensionGroup.id, ); const renamed = await app.invoke("update_extension", { extensionId: extension.id, name: "Renamed Fixture Extension", fileName: null, fileData: null, }); assert.equal(renamed.name, "Renamed Fixture Extension"); assert.equal( await app.invoke("get_extension_icon", { extensionId: extension.id }), null, ); const changedGroup = await app.invoke("update_extension_group", { groupId: extensionGroup.id, name: "Renamed Extension Group", extensionIds: [extension.id], }); assert.equal(changedGroup.name, "Renamed Extension Group"); assert.equal((await app.invoke("list_extensions")).length, 1); assert.equal((await app.invoke("list_extension_groups")).length, 1); await app.invoke("remove_extension_from_group", { groupId: extensionGroup.id, extensionId: extension.id, }); await app.invoke("assign_extension_group_to_profile", { profileId: profile.id, extensionGroupId: null, }); await app.invoke("delete_extension_group", { groupId: extensionGroup.id }); await app.invoke("delete_extension", { extensionId: extension.id }); const vpn = await app.invoke("create_vpn_config_manual", { name: "E2E WireGuard", vpnType: "WireGuard", configData: wireGuardFixture(), }); assert.equal(vpn.name, "E2E WireGuard"); assert.equal( (await app.invoke("get_vpn_config", { vpnId: vpn.id })).id, vpn.id, ); assert.equal((await app.invoke("list_vpn_configs")).length, 1); const updatedVpn = await app.invoke("update_vpn_config", { vpnId: vpn.id, name: "Updated WireGuard", }); assert.equal(updatedVpn.name, "Updated WireGuard"); assert.equal( (await app.invoke("get_vpn_status", { vpnId: vpn.id })).connected, false, ); assert.equal( ( await app.invoke("update_profile_vpn", { profileId: profile.id, vpnId: vpn.id, }) ).vpn_id, vpn.id, ); assert.deepEqual(await app.invoke("list_active_vpn_connections"), []); await app.invoke("disconnect_vpn", { vpnId: vpn.id }); const unknownVpnError = await app.invokeError("check_vpn_validity", { vpnId: "missing-vpn", }); assert.match(unknownVpnError, /not found|Failed to start VPN worker/i); const importedVpn = await app.invoke("import_vpn_config", { content: wireGuardFixture(), filename: "imported.conf", name: "Imported WireGuard", }); assert.equal(importedVpn.success, true); await app.invoke("delete_vpn_config", { vpnId: importedVpn.vpn_id }); await app.invoke("delete_vpn_config", { vpnId: vpn.id }); const dns = await app.invoke("set_custom_dns_config", { sources: [`${process.env.DONUT_E2E_FIXTURE_URL}/dns.txt`], blockDomains: [" Ads.Example.com ", "tracker.example"], allowDomains: ["safe.example"], allowlistMode: false, }); assert.deepEqual(dns.block_domains, ["ads.example.com", "tracker.example"]); const textExport = await app.invoke("export_custom_dns_rules", { format: "txt", }); assert.match(textExport, /ads\.example\.com/); await app.invoke("import_custom_dns_rules", { format: "txt", content: "||malware.example^\n@@||allowed.example^\n", }); const importedDns = await app.invoke("get_custom_dns_config"); assert.ok(importedDns.block_domains.includes("malware.example")); assert.ok(importedDns.allow_domains.includes("allowed.example")); await app.invoke("refresh_dns_blocklists"); const blocklistStatus = await app.invoke("get_dns_blocklist_cache_status"); assert.equal(blocklistStatus.length, 5); assert.ok( blocklistStatus.every( (entry) => entry.is_cached && entry.is_fresh && entry.entry_count === 2, ), ); await app.invoke("delete_profile", { profileId: profile.id }); }); }); test("cookie import/copy/export, profile encryption, and traffic-stat read/clear paths", async () => { await withApp("entities-cookies-password", async (app) => { const source = await createProfile(app, "Cookie Source"); const target = await createProfile(app, "Cookie Target"); const cookieJson = JSON.stringify([ { name: "session", value: "isolated-secret-cookie", domain: "fixture.local", path: "/", secure: false, httpOnly: true, sameSite: "lax", expirationDate: 2_000_000_000, }, ]); const imported = await app.invoke("import_cookies_from_file", { profileId: source.id, content: cookieJson, }); assert.equal(imported.cookies_imported, 1); const cookies = await app.invoke("read_profile_cookies", { profileId: source.id, }); assert.equal(cookies.total_count, 1); assert.equal(cookies.domains[0].cookies[0].value, "isolated-secret-cookie"); const stats = await app.invoke("get_profile_cookie_stats", { profileId: source.id, }); assert.equal(stats.total_count, 1); const copied = await app.invoke("copy_profile_cookies", { request: { source_profile_id: source.id, target_profile_ids: [target.id], selected_cookies: [{ domain: "fixture.local", name: "session" }], }, }); assert.equal(copied[0].cookies_copied, 1); assert.match( await app.invoke("export_profile_cookies", { profileId: target.id, format: "json", }), /isolated-secret-cookie/, ); assert.match( await app.invoke("export_profile_cookies", { profileId: target.id, format: "netscape", }), /fixture\.local/, ); await app.invoke("set_profile_password", { profileId: source.id, password: "correct horse battery staple", }); assert.equal( await app.invoke("is_profile_locked", { profileId: source.id }), false, ); const wrong = await app.invokeError("verify_profile_password", { profileId: source.id, password: "wrong password", }); assert.match(wrong, /INCORRECT_PASSWORD/); await app.invoke("verify_profile_password", { profileId: source.id, password: "correct horse battery staple", }); await app.invoke("change_profile_password", { profileId: source.id, oldPassword: "correct horse battery staple", newPassword: "new correct horse battery staple", }); await app.invoke("lock_profile", { profileId: source.id }); assert.equal( await app.invoke("is_profile_locked", { profileId: source.id }), true, ); await app.invoke("unlock_profile", { profileId: source.id, password: "new correct horse battery staple", }); await app.invoke("remove_profile_password", { profileId: source.id, password: "new correct horse battery staple", }); assert.equal( await app.invoke("is_profile_locked", { profileId: source.id }), false, ); assert.deepEqual(await app.invoke("get_all_traffic_snapshots"), []); assert.equal( await app.invoke("get_profile_traffic_snapshot", { profileId: source.id, }), null, ); assert.equal( await app.invoke("get_traffic_stats_for_period", { profileId: source.id, seconds: 3600, }), null, ); await app.invoke("clear_profile_traffic_stats", { profileId: source.id }); await app.invoke("clear_all_traffic_stats"); await app.invoke("delete_selected_profiles", { profileIds: [source.id, target.id], }); }); });