mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-10-27 08:40:39 +00:00
197 lines
11 KiB
JavaScript
197 lines
11 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import test from 'node:test';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const indexPath = join(__dirname, '..', '..', 'src', 'index.html');
|
|
|
|
async function indexHtml() {
|
|
return readFile(indexPath, 'utf8');
|
|
}
|
|
|
|
test('index page wires Premium 2.0 private-copy unlock flow', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /import \* as Premium2 from '\.\/premium2-helpers\.mjs';/);
|
|
assert.match(html, /window\.ArtstrPremium2 = Premium2;/);
|
|
assert.match(html, /async function _publishPurchaseCopyAndBuildVaultItem/);
|
|
assert.match(html, /helpers\.buildPurchaseCopyPlaintext/);
|
|
assert.match(html, /helpers\.buildPurchaseCopyEventDraft/);
|
|
assert.match(html, /helpers\.buildVaultV2Item/);
|
|
assert.match(html, /await _loadPurchaseCopyPayload\(it\.privateCopy\)/);
|
|
assert.match(html, /source: it\.privateCopy \? 'vault-private-copy' : 'vault'/);
|
|
assert.match(html, /Unlocked\. Saving your private purchased copy/);
|
|
assert.match(html, /Private copy saved\. Syncing purchase vault/);
|
|
});
|
|
|
|
test('index page wires admin policy into premium publish stamping', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /async function _loadPremiumPublishPolicy/);
|
|
assert.match(html, /helpers\.validatePremiumPolicyEvent/);
|
|
assert.match(html, /helpers\.buildPremiumPublishStamp/);
|
|
assert.match(html, /NWC\.verifyEvent\(event\)/);
|
|
assert.match(html, /supportedSoftgateEpochs: Premium\.SUPPORTED_EPOCHS/);
|
|
assert.match(html, /const premiumPolicy = await _loadPremiumPublishPolicy\(\)/);
|
|
assert.match(html, /Premium\.encryptPayload\(payload, coord, \{ epoch: premiumPolicy\.stamp\.softgateEpoch \}\)/);
|
|
assert.match(html, /envelope\.premiumMode = premiumPolicy\.stamp\.premiumMode/);
|
|
assert.match(html, /\.\.\.premiumPolicy\.stamp\.tags/);
|
|
assert.match(html, /\['encrypted', premiumPolicy\.stamp\.encryptedTag\]/);
|
|
});
|
|
|
|
test('index page exposes admin-only premium policy publishing controls', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /id="premiumPolicyAdminDetails"/);
|
|
assert.match(html, /id="premiumPolicyPublishBtn"/);
|
|
assert.match(html, /function isPremiumPolicyAdmin\(\)/);
|
|
assert.match(html, /normalizePubkey\(state\.community\.myPubkey\) === normalizePubkey\(platform\.getPubkeyHex\(\)\)/);
|
|
assert.match(html, /function buildPremiumPolicyFromAdminForm\(\)/);
|
|
assert.match(html, /async function publishPremiumPolicyFromSettings\(\)/);
|
|
assert.match(html, /helpers\.buildPremiumPolicyEventDraft\(policy\)/);
|
|
assert.match(html, /helpers\.validatePremiumPolicyEvent\(signed, \{ platformPubkey: platform\.getPubkeyHex\(\) \}\)/);
|
|
assert.match(html, /placeholder="0 = manual close"/);
|
|
assert.match(html, /claimWindow: defaultClaimDays === 0 \? 'manual' : 'fixed-days'/);
|
|
assert.match(html, /statusEl\.textContent = `Active policy: \$\{policy\.activeSoftgateEpoch \|\| 'unknown'\} \/ claim \$\{policy\.activeClaimEpoch \|\| 'unknown'\} \/ \$\{claimWindow\}\.`/);
|
|
assert.match(html, /on\('premiumPolicyPublishBtn', 'click'/);
|
|
assert.match(html, /Premium policy published/);
|
|
});
|
|
|
|
test('soft-gate crypto supports the active Premium 2.0 policy epoch', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /'2026-07': \[/);
|
|
assert.match(html, /const CURRENT_EPOCH = '2026-07'/);
|
|
});
|
|
|
|
test('index page blocks expired first-time premium claims but keeps owned paths separate', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /function _premiumClaimUntil\(row\)/);
|
|
assert.match(html, /function _premiumClaimStatusForRow\(row\)/);
|
|
assert.match(html, /owned: !!\(row\?\.zapGate\?\.unlocked \|\| isUnlockedLocally\(row\?\.e\?\.id, address\)\)/);
|
|
assert.match(html, /const claimState = _premiumClaimStatusForRow\(row\);\n\s+if \(claimState === 'expired' \|\| claimState === 'closed'\)/);
|
|
assert.match(html, /This premium design is no longer claimable from the public storefront/);
|
|
assert.match(html, /btn\.textContent = 'Claim period ended'/);
|
|
assert.match(html, /forkBtn\.textContent = 'Claim period ended'/);
|
|
assert.match(html, /Purchased private copies still open from the vault/);
|
|
});
|
|
|
|
test('index page persists and retries failed Premium 2.0 private-copy saves', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /const PENDING_PRIVATE_COPY_KEY = 'casewrap-pending-private-copies-v1'/);
|
|
assert.match(html, /function savePendingPrivateCopy/);
|
|
assert.match(html, /function clearPendingPrivateCopy/);
|
|
assert.match(html, /async function retryPendingVaultWrites/);
|
|
assert.match(html, /savePendingPrivateCopy\(\{\n\s+row,/);
|
|
assert.match(html, /clearPendingPrivateCopy\(row\)/);
|
|
assert.match(html, /source: 'pending-private-copy-retry'/);
|
|
assert.match(html, /Retry saving private copy/);
|
|
assert.match(html, /await retryPendingVaultWrites\(\{ silent: false \}\)/);
|
|
assert.match(html, /Use Purchased → Retry saving private copy when relay sync recovers/);
|
|
});
|
|
|
|
test('index page exposes partial-payment resume state without discarding paid legs', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /function partialUnlockSummary\(eventId\)/);
|
|
assert.match(html, /function partialUnlockActionLabel\(eventId, amountSats\)/);
|
|
assert.match(html, /Resume payment \(\$\{summary\.missing\} left\)/);
|
|
assert.match(html, /Artstr will reuse cached invoices\/preimages and only pay missing legs/);
|
|
assert.match(html, /btn\.textContent = partialUnlockActionLabel\(row\.e\?\.id, row\.zapGate\.min\)/);
|
|
assert.match(html, /forkBtn\.textContent = partialUnlockActionLabel\(row\.e\?\.id, row\.zapGate\?\.min \|\| 100\)/);
|
|
assert.match(html, /function clearStalePartialUnlocks/);
|
|
assert.match(html, /if \(partial\?\.creator\?\.preimage \|\| partial\?\.platform\?\.preimage\) continue/);
|
|
assert.match(html, /const stalePartialCleared = clearStalePartialUnlocks\(\)/);
|
|
});
|
|
|
|
test('index page recovers NWC split-zap legs after wallet timeouts', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /const NWC_PAY_INVOICE_TIMEOUT_MS = 60000/);
|
|
assert.match(html, /return _request\('pay_invoice', \{ invoice: bolt11 \}, \{ timeoutMs: opts\.timeoutMs \|\| NWC_PAY_INVOICE_TIMEOUT_MS \}\)/);
|
|
assert.ok(html.includes('const params = /^ln/i.test(value) ? { invoice: value } : { payment_hash: value };'));
|
|
assert.match(html, /const postTimeoutLookup = await nwc\.lookupInvoice\(leg\.bolt11\)/);
|
|
assert.match(html, /if \(postTimeoutLookup\?\.preimage\)/);
|
|
assert.match(html, /preimage: postTimeoutLookup\.preimage/);
|
|
assert.match(html, /If one payment leg completed, click Resume payment/);
|
|
});
|
|
|
|
test('index page refreshes creator Lightning metadata before premium unlock payment', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.ok(html.includes('let authorProfile = _feedCache.profiles?.[row.e?.pubkey]'));
|
|
assert.ok(html.includes('|| state.community?.profiles?.[row.e?.pubkey]'));
|
|
assert.match(html, /if \(!resolveLnurlPayUrl\(authorProfile\)\)/);
|
|
assert.match(html, /authorProfile = await ensureProfilePageData\(row\.e\.pubkey/);
|
|
assert.match(html, /Checking creator Lightning address/);
|
|
assert.match(html, /lud06: authorProfile\?\.lud06/);
|
|
});
|
|
|
|
test('index page migrates legacy inline vault purchases to private copies', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /function _rowFromLegacyVaultItem\(item\)/);
|
|
assert.match(html, /async function migrateLegacyVaultPrivateCopies/);
|
|
assert.match(html, /item\?\.payload && !item\.privateCopy && item\.a && item\.eventId/);
|
|
assert.match(html, /migratedFrom: item\.schema \|\| 'purchase-vault-v1-inline-payload'/);
|
|
assert.match(html, /source: 'vault-legacy-migrated-private-copy'/);
|
|
assert.match(html, /const legacyCount = \(items \|\| \[\]\)\.filter\(\(item\) => item\?\.payload && !item\.privateCopy\)\.length/);
|
|
assert.match(html, /migrateBtn\.textContent = 'Save private copies'/);
|
|
assert.match(html, /await migrateLegacyVaultPrivateCopies\(\{ silent: false \}\)/);
|
|
});
|
|
|
|
test('index page refreshes the Purchased tab after a successful Premium 2.0 purchase', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /function refreshPurchasedTabIfVisible\(opts = \{\}\)/);
|
|
assert.match(html, /if \(_feedState\.filterMine !== 'purchased'\) return/);
|
|
assert.match(html, /loadPurchasedDesignsIntoWall\(wall, opts\)\.catch/);
|
|
assert.match(html, /refreshPurchasedTabIfVisible\(\{ force: false \}\)/);
|
|
});
|
|
|
|
test('index page exposes creator premium epoch refresh for existing listings', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /async function republishPremiumUnderCurrentEpoch\(row\)/);
|
|
assert.match(html, /async function resolvePremiumPayloadForRepublish\(row\)/);
|
|
assert.match(html, /makeFeedCardRefreshEpochButton\(row\)/);
|
|
assert.match(html, /btn\.textContent = needsRefresh \? 'Refresh epoch to sell' : 'Refresh epoch'/);
|
|
assert.match(html, /Epoch refresh needed/);
|
|
assert.match(html, /Refresh epoch to sell/);
|
|
assert.match(html, /This premium storefront is no longer claimable\. Refresh the epoch to make it purchasable again\./);
|
|
assert.match(html, /state\.publish = \{\n\s+\.\.\.\(state\.publish \|\| \{\}\),\n\s+zapGate: true,/);
|
|
assert.match(html, /publishBtn\?\.click\(\)/);
|
|
assert.match(html, /Existing buyers keep their private purchased snapshots/);
|
|
});
|
|
|
|
test('index page exposes Premium 2.0 purchase repair workflow', async () => {
|
|
const html = await indexHtml();
|
|
|
|
assert.match(html, /async function repairPremiumPurchases/);
|
|
assert.match(html, /const pending = await retryPendingVaultWrites\(\{ silent: true \}\)/);
|
|
assert.match(html, /const migrated = await migrateLegacyVaultPrivateCopies\(\{ silent: true \}\)/);
|
|
assert.match(html, /await _loadPurchaseCopyPayload\(item\.privateCopy\)/);
|
|
assert.match(html, /status = pending\.failed \|\| migrated\.failed \|\| unrecoverable \? 'needs_attention' : missing \? 'repairable' : 'clean'/);
|
|
assert.match(html, /repairBtn\.textContent = 'Repair purchases'/);
|
|
assert.match(html, /const result = await repairPremiumPurchases\(\{ silent: false \}\)/);
|
|
assert.match(html, /Purchase repair found everything clean/);
|
|
});
|
|
|
|
test('vault item stubs preserve Premium 2.0 private-copy fields', async () => {
|
|
const html = await indexHtml();
|
|
const stubStart = html.indexOf('function _vaultItemStub(item)');
|
|
const stubEnd = html.indexOf('async function _publishVaultEvent', stubStart);
|
|
const stubBody = html.slice(stubStart, stubEnd);
|
|
|
|
assert.ok(stubStart > -1, '_vaultItemStub should exist');
|
|
assert.ok(stubEnd > stubStart, '_publishVaultEvent should follow _vaultItemStub');
|
|
assert.match(stubBody, /privateCopy: item\.privateCopy/);
|
|
assert.match(stubBody, /privateCopyEventId: item\.privateCopyEventId/);
|
|
assert.match(stubBody, /claimEpoch: item\.claimEpoch/);
|
|
assert.match(stubBody, /receiptSummary: item\.receiptSummary/);
|
|
});
|