import assert from 'node:assert/strict' import crypto from 'node:crypto' import fs from 'node:fs' import test from 'node:test' const read = (path) => fs.readFileSync(new URL('../' + path, import.meta.url), 'utf8') const donors = JSON.parse(read('contracts/web-novel-open-source-donor-assessment.json')) const contract = JSON.parse(read('contracts/web-novel-workspace.json')) const routing = JSON.parse(read('contracts/subdomain-industry-routing.json')) const channelBody = JSON.parse(read('contracts/user-channel-body.json')) const marketplace = JSON.parse(read('contracts/web-novel-module-marketplace-plan.json')) const rust = read('src-tauri/src/web_novel_workspace.rs') const authorRust = read('src-tauri/src/web_novel_author.rs') const moduleRust = read('src-tauri/src/web_novel_modules.rs') const rustLibrary = read('src-tauri/src/lib.rs') const workspaceUi = read('src/WebNovelWorkspace.tsx') const moduleUi = read('src/AuthorModuleCenter.tsx') const mainUi = read('src/main.tsx') test('web novel upstream sources were reviewed at immutable commits through the Singapore relay', () => { assert.equal(donors.state, 'PRIMARY_SOURCE_CODE_REVIEW_COMPLETE_IMPLEMENTATION_BOUNDARY_LOCKED') assert.equal(donors.download_route.relay, 'BS-SG-003') assert.equal(donors.download_route.repositories_are_clean_git_worktrees, true) assert.equal(donors.candidates.length, 6) for (const candidate of donors.candidates) { assert.match(candidate.commit, /^[0-9a-f]{40}$/) assert.ok(candidate.inspected_paths.length > 0) assert.ok(candidate.observed_engine.length > 0) } }) test('license decisions keep GPL source out of the HoloLake implementation', () => { assert.match(donors.candidates.find((item) => item.name === 'novelWriter').decision, /DO_NOT_COPY_GPL_SOURCE/) assert.match(donors.candidates.find((item) => item.name === 'Manuskript').decision, /DO_NOT_COPY_GPL_SOURCE/) assert.equal(donors.implementation_selection.external_source_files_copied, 0) assert.equal(donors.implementation_selection.native_owner, 'HOLOLAKE_TAURI_RUST_CORE') }) test('web novel workspace is account scoped and only projects persona task organs', () => { assert.equal(contract.state, 'NATIVE_ENGINE_AND_FOUR_HOTPLUG_MODULE_CANDIDATES_MIGRATED_ACCEPTANCE_PENDING') assert.equal(contract.domain_entry, 'BRANCH_DOMAIN') assert.equal(contract.industry_number, 'IND-WEBNOVEL-001') assert.equal(contract.native_storage.authenticated_account_required, true) assert.equal(contract.native_storage.cross_account_projection_allowed, false) assert.ok(contract.required_real_engines.includes('CHAPTER_VERSION_HISTORY')) assert.ok(contract.required_real_engines.includes('CONTINUITY_AUDIT')) assert.ok(contract.forbidden_substitutes.includes('UI_ONLY_BUTTONS_WITHOUT_NATIVE_COMMANDS')) assert.equal(contract.ontology_correction.this_contract_is_not, 'USER_CHANNEL_BODY') assert.equal(contract.ontology_correction.author_editor_operator_are_ui_tabs, false) assert.equal(contract.distribution_boundary.preinstalled, 'LIGHT_AUTHOR_WORKBENCH_WITH_REAL_ENGINE') assert.equal(contract.distribution_boundary.current_advanced_features_are_registered_marketplace_modules, false) const industry = routing.industries.find((item) => item.key === 'WEB_NOVEL') assert.equal(industry.industry_number, 'IND-WEBNOVEL-001') assert.equal(industry.initialization_channel.legacy_projection_only, false) assert.equal(industry.initialization_channel.installed_native_modules, 1) assert.equal(industry.initialization_channel.module_slots[0].slot_id, 'WEBNOVEL-AUTHOR-LIGHT') }) test('user channel contract is a durable multi-persona body rather than a module manifest', () => { assert.equal(channelBody.state, 'FOUNDATION_RUNTIME_PARTIAL_ACCEPTANCE_PENDING') assert.equal(channelBody.core_cardinality.language_cores_per_human, 1) assert.equal(channelBody.core_cardinality.persona_cores_per_language_core, 1) assert.equal(channelBody.core_cardinality.persona_entities_per_persona_core, 'MANY') assert.equal(channelBody.persona_entity_boundary.role_template_creates_mature_persona, false) assert.equal(channelBody.persona_entity_boundary.task_agent_is_persona, false) assert.equal(channelBody.task_projection.workbench_is_channel_body, false) assert.equal(channelBody.built_in_organs_policy.built_in_organs_allowed, true) assert.equal(channelBody.module_ecosystem.official_module_number_before_real_acceptance, false) assert.ok(channelBody.forbidden_substitutes.includes('DECLARATIVE_MODULE_MANIFEST_AS_CHANNEL_BODY')) assert.ok(channelBody.acceptance_required.includes('TWO_PERSONAS_WITH_ISOLATED_GROWTH_MEMORY')) }) test('the lightweight author workbench is built in while complete author capabilities stay numbered marketplace candidates', () => { assert.equal(marketplace.built_in_light_author_workbench.distribution, 'PREINSTALLED') assert.equal(marketplace.built_in_light_author_workbench.real_engine_owner, 'HOLOLAKE_NATIVE_RUST_SQLITE') assert.ok(marketplace.built_in_light_author_workbench.capabilities.includes('REAL_CHAPTER_TEXT_EDITING')) assert.equal(marketplace.official_module_candidates.length, 4) for (const candidate of marketplace.official_module_candidates) { assert.match(candidate.module_number, /^HL-MOD-WEBNOVEL-/) assert.equal(candidate.numbering_state, 'CANDIDATE_NUMBER_NOT_OFFICIALLY_REGISTERED') } assert.deepEqual(marketplace.publication_gate.slice(-3), [ 'THEN_ASSIGN_PERMANENT_MODULE_NUMBER', 'THEN_REGISTER_OFFICIAL_MODULE_REGISTRY', 'THEN_PUBLISH_OFFICIAL_MARKETPLACE', ]) assert.ok(marketplace.channel_deployment_flow.includes('MOUNT_IN_CURRENT_CHANNEL')) assert.equal(marketplace.data_boundary.uninstall_preserves_story_data_and_receipts, true) }) test('the desktop workbench invokes a registered native engine for every primary workflow', () => { const commands = [ 'get_web_novel_workspace_snapshot', 'create_web_novel_work', 'create_web_novel_volume', 'create_web_novel_chapter', 'save_web_novel_chapter', 'transition_web_novel_chapter', 'create_web_novel_checkpoint', 'restore_web_novel_checkpoint', 'upsert_web_novel_story_entity', 'create_web_novel_story_relation', 'upsert_web_novel_foreshadow', 'create_web_novel_review_note', 'resolve_web_novel_review_note', 'save_web_novel_metric', 'run_web_novel_continuity_audit', 'export_web_novel_markdown', ] for (const command of commands) { assert.match(rust, new RegExp(`fn ${command}`), `missing Rust implementation: ${command}`) assert.match(rustLibrary, new RegExp(`web_novel_workspace::${command}`), `missing Tauri registration: ${command}`) assert.match(workspaceUi, new RegExp(`['\"]${command}['\"]`), `missing UI invocation: ${command}`) } assert.match(rust, /account_storage_root\(app, "web-novel-workspace-v1"\)/) assert.match(rust, /HOLOLAKE_WEBNOVEL_CHAPTER_REVISION_CONFLICT/) assert.match(rust, /web_novel_chapter_versions/) assert.match(workspaceUi, /window\.setTimeout\(\(\) => void flushChapterSave\(\), 650\)/) assert.match(workspaceUi, /role=\{createFeedback\.state === 'error' \? 'alert' : 'status'\}/) assert.match(workspaceUi, /正在建立作品与第一卷/) assert.match(workspaceUi, /作品没有创建:/) assert.match(workspaceUi, /第一卷已经可以开始写作/) assert.match(mainUi, /worldStage === 'webNovelWorkspace'/) }) test('author formatting is automatic on import, selectable, reversible, and can cover the whole work', () => { for (const command of ['format_web_novel_chapter', 'format_web_novel_work']) { assert.match(authorRust, new RegExp(`fn ${command}`)) assert.match(rustLibrary, new RegExp(`web_novel_author::${command}`)) assert.match(workspaceUi, new RegExp(`['"]${command}['"]`)) } assert.match(authorRust, /formatted\.join\("\\n\\n"\)/) assert.match(read('src-tauri/src/web_novel_import.rs'), /AUTO_FORMAT_ON_IMPORT/) assert.match(workspaceUi, /细纲结构视图/) assert.match(read('src/AuthorWritingSidecar.tsx'), /一键排版整部作品/) }) test('author modules are immutable packages with native install mount data and uninstall engines', () => { const modules = [ ['HL-MOD-WEBNOVEL-OUTLINE-001', '6d31d3f530862000bd84a8f5dc73bcb9cf0d5738219f7a287562d4f4bcfa5318'], ['HL-MOD-WEBNOVEL-GRID-002', '2e34e0337c51dbbdd1efdd807be71e69a0cb2a1745624e1f51b452b7b54f1553'], ['HL-MOD-WEBNOVEL-STORYWORLD-003', 'd486719500ddfc08054d9c9bb6d3683afe5547a2b24c9b04d0f899ac66094d2f'], ['HL-MOD-WEBNOVEL-DELIVERY-004', '4af1d4610826edd29759bd0dc12b4b0c04fd2ebebb4ebc362d47d762810989e8'], ] for (const [moduleId, expectedSha] of modules) { const bytes = fs.readFileSync(new URL(`../official-modules/web-novel/${moduleId}/1.0.0/module.json`, import.meta.url)) const manifest = JSON.parse(bytes.toString('utf8')) assert.equal(manifest.moduleId, moduleId) assert.equal(crypto.createHash('sha256').update(bytes).digest('hex'), expectedSha) assert.match(moduleRust, new RegExp(expectedSha)) } assert.match(moduleRust, /web_novel_module_installations/) assert.match(moduleRust, /web_novel_module_receipts/) assert.match(moduleRust, /require_mounted/) assert.match(moduleRust, /USER_DATA_REMAINS_AFTER_UNINSTALL|作品、模块数据和历史回执保持/) assert.match(moduleRust, /RESTORED_FROM_VERSION/) assert.match(moduleRust, /render_docx/) assert.match(moduleRust, /render_epub/) }) test('every author module action is registered and invoked by the live channel workbench', () => { const commands = [ 'get_web_novel_author_module_marketplace', 'install_web_novel_author_module', 'mount_web_novel_author_module', 'unmount_web_novel_author_module', 'uninstall_web_novel_author_module', 'get_web_novel_author_module_data', 'upsert_web_novel_author_scene', 'upsert_web_novel_author_beat', 'upsert_web_novel_story_field_definition', 'upsert_web_novel_story_field_value', 'upsert_web_novel_timeline_event', 'link_web_novel_scene_entity', 'restore_web_novel_chapter_version', 'export_web_novel_author_delivery', ] for (const command of commands) { assert.match(moduleRust, new RegExp(`fn ${command}`), `missing module engine: ${command}`) assert.match(rustLibrary, new RegExp(`web_novel_modules::${command}`), `missing module registration: ${command}`) assert.match(moduleUi, new RegExp(`['"]${command}['"]`), `missing module UI invocation: ${command}`) } assert.match(workspaceUi, /