/* * Copyright (C) 2024-present Puter Technologies Inc. * * This file is part of Puter. * * Puter is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ // Setup for the `shared-worker` vitest project, whose files run with // `isolate: false` and so share one module graph per worker. Resets the // process-wide state that a fresh worker would otherwise have provided. import MockRedis from 'ioredis-mock'; import { afterAll, beforeAll, vi } from 'vitest'; import { configContainer } from './exports'; import { extensionStore } from './extensions'; // Loaded before the snapshot so anything registered at import time is part // of the pristine state. import './server'; type Container = unknown[] | Map | Record; // Copies one level deeper than the container: event listener lists are // arrays inside `events`, and registering pushes into them. const copyContainer = (value: Container): Container => { if (Array.isArray(value)) return [...value]; if (value instanceof Map) return new Map(value); return Object.fromEntries( Object.entries(value).map(([k, v]) => [ k, Array.isArray(v) ? [...v] : v, ]), ); }; // This file is re-evaluated for every test file, so the pristine snapshot // lives on globalThis, taken before the first file in the worker runs. const SNAPSHOT_KEY = Symbol.for('puter.test.extensionStoreSnapshot'); const globals = globalThis as { [SNAPSHOT_KEY]?: Record; }; globals[SNAPSHOT_KEY] ??= Object.fromEntries( Object.entries(extensionStore).map(([k, v]) => [ k, copyContainer(v as Container), ]), ); const restoreExtensionStore = () => { for (const [key, pristine] of Object.entries(globals[SNAPSHOT_KEY]!)) { // The server reads these containers by reference, so refill in place. const live = (extensionStore as unknown as Record)[ key ]; const fresh = copyContainer(pristine); if (Array.isArray(live)) { live.splice(0, live.length, ...(fresh as unknown[])); } else if (live instanceof Map) { live.clear(); for (const [k, v] of fresh as Map) live.set(k, v); } else { for (const k of Object.keys(live)) delete live[k]; Object.assign(live, fresh); } } }; beforeAll(async () => { // Every mock client shares the default host/port store, so cached rows // from a previous file's server would otherwise survive. const redis = new MockRedis(); await redis.flushall(); redis.disconnect(); for (const key of Object.keys(configContainer)) { delete (configContainer as unknown as Record)[key]; } restoreExtensionStore(); }); afterAll(() => { vi.unstubAllEnvs(); vi.unstubAllGlobals(); vi.restoreAllMocks(); vi.useRealTimers(); });