From 7531a946872fe5ec9926a60359296a828930125a Mon Sep 17 00:00:00 2001 From: KernelDeimos <7225168+KernelDeimos@users.noreply.github.com> Date: Mon, 5 Jan 2026 21:30:52 -0500 Subject: [PATCH] clean(data-access): remove bad fixes and simplify --- .../data-access/AppService.comp.test.js | 191 ++---------------- src/backend/src/util/esmcontext.js | 28 +++ 2 files changed, 45 insertions(+), 174 deletions(-) create mode 100644 src/backend/src/util/esmcontext.js diff --git a/src/backend/src/modules/data-access/AppService.comp.test.js b/src/backend/src/modules/data-access/AppService.comp.test.js index df294cfc8..6e58f2031 100644 --- a/src/backend/src/modules/data-access/AppService.comp.test.js +++ b/src/backend/src/modules/data-access/AppService.comp.test.js @@ -20,137 +20,6 @@ import AppService from './AppService'; import { describe, expect, it } from 'vitest'; -/* -// CRITICAL: Mock BOTH Context modules (CommonJS and ESM) BEFORE any other imports -// CommonJS modules use '../../util/context', ESM modules use '../../util/esmcontext.js' -vi.mock('../../util/context', async () => { - const actual = await vi.importActual('../../util/context'); - const { Context: OriginalContext } = actual; - - // Store original get method BEFORE patching - const originalGet = OriginalContext.get; - - // Store test values - let testContext = null; - let testActor = null; - let testUser = null; - - // Patch Context.get to use test values - OriginalContext.get = function(key, options) { - // Check test values FIRST - if (key === 'actor' && testActor) { - return testActor; - } - if (key === 'user' && testUser) { - return testUser; - } - - // Call original get method - return originalGet.call(this, key, options); - }; - - // Export patched Context with setters for test values - return { - ...actual, - Context: OriginalContext, - __setTestValues: (ctx, actor, user) => { - testContext = ctx; - testActor = actor; - testUser = user; - }, - __clearTestValues: () => { - testContext = null; - testActor = null; - testUser = null; - }, - }; -}); - -vi.mock('../../util/esmcontext.js', async () => { - const actual = await vi.importActual('../../util/esmcontext.js'); - const { Context: OriginalContext } = actual; - - // Store original get method BEFORE patching - const originalGet = OriginalContext.get; - - // Store test values - let testContext = null; - let testActor = null; - let testUser = null; - - // Patch Context.get to use test values - OriginalContext.get = function(key, options) { - // Check test values FIRST - if (key === 'actor' && testActor) { - return testActor; - } - if (key === 'user' && testUser) { - return testUser; - } - - // Call original get method - return originalGet.call(this, key, options); - }; - - // Export patched Context with setters for test values - return { - ...actual, - Context: OriginalContext, - __setTestValues: (ctx, actor, user) => { - testContext = ctx; - testActor = actor; - testUser = user; - }, - __clearTestValues: () => { - testContext = null; - testActor = null; - testUser = null; - }, - }; -}); - -// Store test values globally - this is the most reliable approach -let globalTestActor = null; -let globalTestUser = null; - -// Patch Context.get directly (after imports) - this MUST work -// We patch it on the actual Context class that's imported -const originalContextGet = Context.get; -Context.get = function(key, options) { - // ALWAYS check global test values FIRST - this is the most reliable - if (key === 'actor' && globalTestActor) { - return globalTestActor; - } - if (key === 'user' && globalTestUser) { - return globalTestUser; - } - // Call original - return originalContextGet.call(this, key, options); -}; - -// Also patch the Context class that CommonJS modules might have cached -// Intercept require() calls to context.js and ensure they get our patched version -const Module = require('module'); -const originalRequire = Module.prototype.require; -Module.prototype.require = function(id) { - const result = originalRequire.call(this, id); - // If this is context.js, patch its Context.get - if (id.includes('util/context') && result && result.Context) { - const originalGet = result.Context.get; - result.Context.get = function(key, options) { - if (key === 'actor' && globalTestActor) { - return globalTestActor; - } - if (key === 'user' && globalTestUser) { - return globalTestUser; - } - return originalGet.call(this, key, options); - }; - } - return result; -}; -*/ - const ES_APP_ARGS = { entity: 'app', upstream: ESBuilder.create([ @@ -182,16 +51,15 @@ const ES_APP_ARGS = { ]), }; +// Fix: Manually initialize AsyncLocalStorage store for Vitest +// Under Vitest, AsyncLocalStorage may not have a store initialized, causing Context.get() to fail. +// This manually creates a store and sets the root context, ensuring Context operations work. +// This may be a side-effect of OpenTelemetry's own use of AsyncLocalStorage. const fixContextInitialization = async (callback) => { - process.stdout.write(`contextAsyncLocalStorage:${ Context.contextAsyncLocalStorage }\n`); - try { - return Context.contextAsyncLocalStorage.run(Context.root, () => { - Context.contextAsyncLocalStorage.getStore().set('context', Context.root); - callback(); - }); - } catch (e) { - process.stdout.write(e.stack); - } + return await Context.contextAsyncLocalStorage.run(Context.root, async () => { + Context.contextAsyncLocalStorage.getStore().set('context', Context.root); + return await callback(); + }); }; const testWithEachService = async (fnToRunOnBoth) => { @@ -244,8 +112,6 @@ describe('AppService Regression Prevention Tests', () => { it('should create the app', async () => { await fixContextInitialization(async () => { await testWithEachService(async ({ kernel, key }) => { - // Context initialization fix - // Create a test user and context const db = kernel.services.get('database').get('write', 'test'); const userId = 1; @@ -284,38 +150,15 @@ describe('AppService Regression Prevention Tests', () => { await userContext.arun(async () => { Context.set('actor', actor); - // Set test values in BOTH mocked Context modules AND globally - // globalTestActor = actor; - // globalTestUser = user; - - const contextCJS = require('../../util/context'); - const contextESM = await import('../../util/esmcontext.js'); - if ( contextCJS.__setTestValues ) contextCJS.__setTestValues(userContext, actor, user); - if ( contextESM.__setTestValues ) contextESM.__setTestValues(userContext, actor, user); - - process.stdout.write(`actor: ${actor}\n`); - process.stdout.write(`context root from test: ${Context.get('rootContextUUID')}\n`); - - try { - const service = kernel.services.get(key); - const crudQ = service.constructor.IMPLEMENTS['crud-q']; - await crudQ.create.call(service, { - object: { - name: 'test-app', - title: 'Test App', - index_url: 'https://example.com', - }, - }); - } finally { - // Clear after test - // globalTestActor = null; - // globalTestUser = null; - - const contextCJS = require('../../util/context'); - const contextESM = await import('../../util/esmcontext.js'); - if ( contextCJS.__clearTestValues ) contextCJS.__clearTestValues(); - if ( contextESM.__clearTestValues ) contextESM.__clearTestValues(); - } + const service = kernel.services.get(key); + const crudQ = service.constructor.IMPLEMENTS['crud-q']; + await crudQ.create.call(service, { + object: { + name: 'test-app', + title: 'Test App', + index_url: 'https://example.com', + }, + }); }); }); }); diff --git a/src/backend/src/util/esmcontext.js b/src/backend/src/util/esmcontext.js new file mode 100644 index 000000000..3b7bb8ebb --- /dev/null +++ b/src/backend/src/util/esmcontext.js @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2026-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 . + */ + +// Bridge file to ensure ES modules and CommonJS modules use the same Context instance +// This file uses require() to load context.js, ensuring compatibility with +// CommonJS modules that also require() context.js +const { Context, ContextExpressMiddleware } = require('./context.js'); + +module.exports = { + Context, + ContextExpressMiddleware, +};