From b81c424045ccb874a756927a7357885a902cc8e0 Mon Sep 17 00:00:00 2001 From: Van Buren Date: Thu, 2 Apr 2026 06:36:01 -0400 Subject: [PATCH 01/31] feat: lint --- .github/workflows/pre-commit.yml | 41 + .husky/commit-msg | 2 + .husky/pre-commit | 12 + .pre-commit-config.yaml | 65 ++ biome.json | 72 ++ client/src/components/ExampleComponent.tsx | 55 +- client/src/components/ui/button.tsx | 6 +- client/src/declarations.d.ts | 6 +- client/src/pages/ErrorPage.tsx | 4 +- java/src/reactors/AbstractProjectReactor.java | 7 +- mcp/pixel_mcp.json | 48 +- mcp/py_mcp.json | 128 ++- package.json | 19 + pnpm-lock.yaml | 745 ++++++++++++++++++ pom.xml | 3 +- py/mcp_driver.py | 25 +- 16 files changed, 1106 insertions(+), 132 deletions(-) create mode 100644 .github/workflows/pre-commit.yml create mode 100755 .husky/commit-msg create mode 100644 .husky/pre-commit create mode 100644 .pre-commit-config.yaml create mode 100644 biome.json create mode 100644 package.json create mode 100644 pnpm-lock.yaml diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..d1c53ca --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,41 @@ +name: Pre-commit + +on: + pull_request: + branches: [ default-app ] + workflow_dispatch: + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Install dependencies and run frontend linting + run: | + if [ -d "client" ]; then + pnpm install + pnpm exec biome format --write && pnpm exec biome lint + else + echo "No client directory found, skipping frontend linting" + fi + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12.9' + + # Official pre-commit action handles caching automatically + - uses: pre-commit/action@v3.0.1 + with: + extra_args: --all-files --show-diff-on-failure diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..6e3c7d8 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,2 @@ +# Run pre-commit hooks for commit-msg stage +pre-commit run --hook-stage commit-msg --commit-msg-filename "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..d8f48dd --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,12 @@ +# Lint FE code +echo "Formatting and linting FE code..." +pnpm exec lint-staged + +# Capture list of staged files +STAGED_FILES=$(git diff --name-only --cached) + +# Format BE code +echo "Formatting BE code..." +pre-commit run --all-files + +# You may want to consider also running `pre-commit autoupdate` periodically to keep hooks updated. \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4bbdd52 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,65 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-merge-conflict + - id: end-of-file-fixer + files: '^(java/.*\.java|py.*\.py)$' + - id: trailing-whitespace + files: '^(java/.*\.java|py.*\.py)$' + - id: mixed-line-ending + args: ['--fix=auto'] + files: '^(java/.*\.java|py.*\.py)$' + - id: check-yaml + - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.15.0 + hooks: + - id: pretty-format-java + args: [--autofix, --google-java-formatter-version=1.28.0] + files: '^java/.*\.java$' + - repo: https://github.com/commitizen-tools/commitizen + rev: v3.10.0 + hooks: + - id: commitizen + stages: [commit-msg] + - repo: https://github.com/psf/black + rev: 23.9.1 + hooks: + - id: black + language_version: python3 + files: '^py.*\.py$' + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + args: ['--profile', 'black'] + files: '^py.*\.py$' + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.18.2 + hooks: + - id: mypy + files: '\.py$' + args: [ + "--ignore-missing-imports", + "--show-error-codes", + "--explicit-package-bases", + "--allow-untyped-defs", + "--allow-incomplete-defs", + "--allow-untyped-calls", + "--no-strict-optional", + "--disable-error-code=misc", + "--disable-error-code=no-any-return", + "--disable-error-code=name-defined", + "--disable-error-code=attr-defined" + ] + additional_dependencies: [ # Continue to add dependencies here that have mypy extensions, such as pydantic + "types-requests", + "types-toml", + "pandas-stubs", + "numpy" + ] + - repo: https://github.com/pamoller/xmlformatter + rev: v0.2.8 + hooks: + - id: xml-formatter + files: '^.*\.xml$' diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..1a1663f --- /dev/null +++ b/biome.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "formatter": { + "enabled": true, + "lineEnding": "lf", + "indentWidth": 4 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "complexity": { + "noUselessFragments": "error" + }, + "correctness": { + "noUnusedVariables": "error", + "noUnusedImports": "error", + "useExhaustiveDependencies": { + "fix": "none", + "level": "error", + "options": {} + } + }, + "style": { + "useConst": "error" + }, + "suspicious": { + "noExplicitAny": "error", + "noConsole": "warn", + "noVar": "error" + } + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "css": { + "parser": { + "tailwindDirectives": true + } + }, + "files": { + "includes": [ + "client/**/*", + "!node_modules/**/*", + "**/*.{js,ts,mjs,cjs,jsx,tsx,json,css,scss,md,mdx,html,yaml}", + "!.history/*" + ] + }, + "overrides": [ + { + "includes": ["**/*d.ts"], + "linter": { + "rules": { + "correctness": { + "noUnusedVariables": "off" + } + } + } + } + ] +} diff --git a/client/src/components/ExampleComponent.tsx b/client/src/components/ExampleComponent.tsx index 7dfb5ae..42ea12e 100644 --- a/client/src/components/ExampleComponent.tsx +++ b/client/src/components/ExampleComponent.tsx @@ -37,24 +37,27 @@ export const ExampleComponent = () => { // Call the GetWeather reactor via a Pixel command. // Pixel is the SEMOSS query language. Reactor names drop the "Reactor" suffix: // GetWeatherReactor -> GetWeather(city=["value"]) - const handleGetForecast = useCallback(async (city: string) => { - setIsRunning(true); - try { - const { pixelReturn } = await actions.run<[string]>( - `GetWeather(city=${JSON.stringify(city)})`, - ); + const handleGetForecast = useCallback( + async (city: string) => { + setIsRunning(true); + try { + const { pixelReturn } = await actions.run<[string]>( + `GetWeather(city=${JSON.stringify(city)})`, + ); - if (pixelReturn[0].operationType.includes("ERROR")) { - throw new Error(pixelReturn[0].output); - } + if (pixelReturn[0].operationType.includes("ERROR")) { + throw new Error(pixelReturn[0].output); + } - setForecast(pixelReturn[0].output); - } catch (e) { - toast.error(`Failed to get forecast: ${e.message}`); - } finally { - setIsRunning(false); - } - }, [actions]); + setForecast(pixelReturn[0].output); + } catch (e) { + toast.error(`Failed to get forecast: ${e.message}`); + } finally { + setIsRunning(false); + } + }, + [actions], + ); // Send the result back to the Playground chat. // The SDK handles matching this response to the correct MCP tool invocation. @@ -72,18 +75,21 @@ export const ExampleComponent = () => { if (tool.tool_response) { // Viewing a past execution — restore the previous result setForecast(tool.tool_response); - setCity((tool.executedParameters?.city || tool.parameters?.city) as string || ""); + setCity( + ((tool.executedParameters?.city || + tool.parameters?.city) as string) || "", + ); setHasSentToChat(true); } else { // Fresh MCP invocation — auto-fill inputs and optionally auto-run - const cityFromParams = tool.parameters?.city as string || ""; + const cityFromParams = (tool.parameters?.city as string) || ""; setCity(cityFromParams); if (cityFromParams) { handleGetForecast(cityFromParams); } } } - }, [tool]); + }, [tool, handleGetForecast]); const disabled = isRunning || hasSentToChat; @@ -102,7 +108,10 @@ export const ExampleComponent = () => { /> - @@ -118,7 +127,11 @@ export const ExampleComponent = () => { /> - diff --git a/client/src/components/ui/button.tsx b/client/src/components/ui/button.tsx index 7822242..083b466 100644 --- a/client/src/components/ui/button.tsx +++ b/client/src/components/ui/button.tsx @@ -8,15 +8,15 @@ const buttonVariants = cva( { variants: { variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", + default: + "bg-primary text-primary-foreground hover:bg-primary/90", destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: - "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", link: "text-primary underline-offset-4 hover:underline", }, size: { diff --git a/client/src/declarations.d.ts b/client/src/declarations.d.ts index 9eed7ca..6fe092c 100644 --- a/client/src/declarations.d.ts +++ b/client/src/declarations.d.ts @@ -3,9 +3,9 @@ // Environment variables available via import.meta.env (configured in .env and .env.local) interface ImportMetaEnv { - readonly ENDPOINT: string; // SEMOSS server URL (e.g. http://localhost:9090) - readonly MODULE: string; // API module path (e.g. /Monolith) - readonly APP: string; // App/project ID + readonly ENDPOINT: string; // SEMOSS server URL (e.g. http://localhost:9090) + readonly MODULE: string; // API module path (e.g. /Monolith) + readonly APP: string; // App/project ID readonly VITE_ACCESS_KEY: string; // Local dev auth (not used in production) readonly VITE_SECRET_KEY: string; // Local dev auth (not used in production) } diff --git a/client/src/pages/ErrorPage.tsx b/client/src/pages/ErrorPage.tsx index 41b48a7..43ba0fc 100644 --- a/client/src/pages/ErrorPage.tsx +++ b/client/src/pages/ErrorPage.tsx @@ -8,8 +8,8 @@ export const ErrorPage = () => {
- An error has occurred. Please try again or contact support if the - problem persists. + An error has occurred. Please try again or contact support if + the problem persists.
); diff --git a/java/src/reactors/AbstractProjectReactor.java b/java/src/reactors/AbstractProjectReactor.java index 0681fef..02cd038 100644 --- a/java/src/reactors/AbstractProjectReactor.java +++ b/java/src/reactors/AbstractProjectReactor.java @@ -22,7 +22,8 @@ // // To create a new reactor: // 1. Create a new class in this folder extending AbstractProjectReactor -// 2. Define keysToGet (parameter names) and keyRequired (1=required, 0=optional) in the constructor +// 2. Define keysToGet (parameter names) and keyRequired (1=required, 0=optional) in the +// constructor // 3. Implement doExecute() with your business logic // 4. Access parameters via this.keyValue.get("paramName") after organizeKeys() runs // 5. Return results as NounMetadata (strings, maps, etc.) @@ -33,8 +34,8 @@ public abstract class AbstractProjectReactor extends AbstractReactor { private static final Logger LOGGER = LogManager.getLogger(AbstractProjectReactor.class); // These protected variables are available in all subclass reactors - protected User user; // The authenticated user running this reactor - protected String projectId; // The SEMOSS project/app ID + protected User user; // The authenticated user running this reactor + protected String projectId; // The SEMOSS project/app ID protected ProjectProperties projectProperties; // Values from java/project.properties // TODO: Initialize additional protected variables (engines, external services, diff --git a/mcp/pixel_mcp.json b/mcp/pixel_mcp.json index 52d7a47..248d764 100644 --- a/mcp/pixel_mcp.json +++ b/mcp/pixel_mcp.json @@ -1,23 +1,27 @@ { - "_meta": {"last_modified_date": "2026-03-10"}, - "tools": [{ - "inputSchema": { - "type": "object", - "title": "Weather_Arguments", - "properties": {"city": { - "description": "The city to get the weather forecast for.", - "title": "city", - "type": "string" - }}, - "required": ["city"] - }, - "name": "GetWeather", - "description": "Get the weather forecast for a city.", - "_meta": { - "SMSS_MCP_UI": {"resourceURI": "/#/"}, - "SMSS_FUNCTION_NAME": "GetWeather", - "SMSS_MCP_EXECUTION": "ask" - }, - "title": "GetWeather" - }] -} \ No newline at end of file + "_meta": { "last_modified_date": "2026-03-10" }, + "tools": [ + { + "inputSchema": { + "type": "object", + "title": "Weather_Arguments", + "properties": { + "city": { + "description": "The city to get the weather forecast for.", + "title": "city", + "type": "string" + } + }, + "required": ["city"] + }, + "name": "GetWeather", + "description": "Get the weather forecast for a city.", + "_meta": { + "SMSS_MCP_UI": { "resourceURI": "/#/" }, + "SMSS_FUNCTION_NAME": "GetWeather", + "SMSS_MCP_EXECUTION": "ask" + }, + "title": "GetWeather" + } + ] +} diff --git a/mcp/py_mcp.json b/mcp/py_mcp.json index 5f22ecd..45c23a4 100644 --- a/mcp/py_mcp.json +++ b/mcp/py_mcp.json @@ -1,67 +1,63 @@ { - "_meta": { - "last_modified_date": "2026-03-11", - "file_last_modified_date": "2026-03-11", - "source_file": "/Users/tevanburen/Documents/SEMOSS/workspace/Semoss/project/Blank Canvas__9447b875-266e-4814-a1de-c840ff88401a/app_root/version/assets/py/mcp_driver.py" - }, - "tools": [ - { - "name": "fahrenheit_to_celsius", - "title": "Fahrenheit To Celsius", - "description": "Convert a temperature from Fahrenheit to Celsius.", - "inputSchema": { - "properties": { - "temperature_f": { - "title": "Temperature F", - "description": "No docstring present or unable to parse docstring from function", - "type": "number" - } - }, - "required": [ - "temperature_f" - ], - "title": "Fahrenheit To Celsius Arguments", - "type": "object" - }, - "_meta": { - "generated_on": "2026-03-11", - "SMSS_MCP_EXECUTION": "auto", - "SMSS_MCP_UI": { - "displayLocation": "inline", - "loadingMessage": "Converting temperature..." - }, - "SMSS_FUNCTION_NAME": "fahrenheit_to_celsius" - }, - "_type": "python" - }, - { - "name": "celsius_to_fahrenheit", - "title": "Celsius To Fahrenheit", - "description": "Convert a temperature from Celsius to Fahrenheit.", - "inputSchema": { - "properties": { - "temperature_c": { - "title": "Temperature C", - "description": "No docstring present or unable to parse docstring from function", - "type": "number" - } - }, - "required": [ - "temperature_c" - ], - "title": "Celsius To Fahrenheit Arguments", - "type": "object" - }, - "_meta": { - "generated_on": "2026-03-11", - "SMSS_MCP_EXECUTION": "auto", - "SMSS_MCP_UI": { - "displayLocation": "inline", - "loadingMessage": "Converting temperature..." - }, - "SMSS_FUNCTION_NAME": "celsius_to_fahrenheit" - }, - "_type": "python" - } - ] -} \ No newline at end of file + "_meta": { + "last_modified_date": "2026-03-11", + "file_last_modified_date": "2026-03-11", + "source_file": "/Users/tevanburen/Documents/SEMOSS/workspace/Semoss/project/Blank Canvas__9447b875-266e-4814-a1de-c840ff88401a/app_root/version/assets/py/mcp_driver.py" + }, + "tools": [ + { + "name": "fahrenheit_to_celsius", + "title": "Fahrenheit To Celsius", + "description": "Convert a temperature from Fahrenheit to Celsius.", + "inputSchema": { + "properties": { + "temperature_f": { + "title": "Temperature F", + "description": "No docstring present or unable to parse docstring from function", + "type": "number" + } + }, + "required": ["temperature_f"], + "title": "Fahrenheit To Celsius Arguments", + "type": "object" + }, + "_meta": { + "generated_on": "2026-03-11", + "SMSS_MCP_EXECUTION": "auto", + "SMSS_MCP_UI": { + "displayLocation": "inline", + "loadingMessage": "Converting temperature..." + }, + "SMSS_FUNCTION_NAME": "fahrenheit_to_celsius" + }, + "_type": "python" + }, + { + "name": "celsius_to_fahrenheit", + "title": "Celsius To Fahrenheit", + "description": "Convert a temperature from Celsius to Fahrenheit.", + "inputSchema": { + "properties": { + "temperature_c": { + "title": "Temperature C", + "description": "No docstring present or unable to parse docstring from function", + "type": "number" + } + }, + "required": ["temperature_c"], + "title": "Celsius To Fahrenheit Arguments", + "type": "object" + }, + "_meta": { + "generated_on": "2026-03-11", + "SMSS_MCP_EXECUTION": "auto", + "SMSS_MCP_UI": { + "displayLocation": "inline", + "loadingMessage": "Converting temperature..." + }, + "SMSS_FUNCTION_NAME": "celsius_to_fahrenheit" + }, + "_type": "python" + } + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..eefd886 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "private": true, + "scripts": { + "fix": "biome format --write && biome lint && pre-commit run --all-files || pre-commit run --all-files", + "dev": "pnpm --dir client dev" + }, + "devDependencies": { + "@biomejs/biome": "2.4.10", + "http-server": "^14.1.1", + "husky": "^9.1.7", + "lint-staged": "^16.4.0" + }, + "lint-staged": { + "**/*.{js,jsx,ts,tsx,json,html,css}": [ + "biome format --write", + "biome lint" + ] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..6224996 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,745 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@biomejs/biome': + specifier: 2.4.10 + version: 2.4.10 + http-server: + specifier: ^14.1.1 + version: 14.1.1 + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^16.4.0 + version: 16.4.0 + +packages: + + '@biomejs/biome@2.4.10': + resolution: {integrity: sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.4.10': + resolution: {integrity: sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.4.10': + resolution: {integrity: sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.4.10': + resolution: {integrity: sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-arm64@2.4.10': + resolution: {integrity: sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-linux-x64-musl@2.4.10': + resolution: {integrity: sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-x64@2.4.10': + resolution: {integrity: sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-win32-arm64@2.4.10': + resolution: {integrity: sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.4.10': + resolution: {integrity: sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + corser@2.0.1: + resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} + engines: {node: '>= 0.4.0'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-east-asian-width@1.5.0: + resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + http-server@14.1.1: + resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==} + engines: {node: '>=12'} + hasBin: true + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + lint-staged@16.4.0: + resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + portfinder@1.0.38: + resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==} + engines: {node: '>= 10.12'} + + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + engines: {node: '>=0.6'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + secure-compare@3.0.1: + resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.0: + resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + engines: {node: '>=20'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tinyexec@1.0.4: + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + engines: {node: '>=18'} + + union@0.5.0: + resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} + engines: {node: '>= 0.8.0'} + + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} + hasBin: true + +snapshots: + + '@biomejs/biome@2.4.10': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.4.10 + '@biomejs/cli-darwin-x64': 2.4.10 + '@biomejs/cli-linux-arm64': 2.4.10 + '@biomejs/cli-linux-arm64-musl': 2.4.10 + '@biomejs/cli-linux-x64': 2.4.10 + '@biomejs/cli-linux-x64-musl': 2.4.10 + '@biomejs/cli-win32-arm64': 2.4.10 + '@biomejs/cli-win32-x64': 2.4.10 + + '@biomejs/cli-darwin-arm64@2.4.10': + optional: true + + '@biomejs/cli-darwin-x64@2.4.10': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.4.10': + optional: true + + '@biomejs/cli-linux-arm64@2.4.10': + optional: true + + '@biomejs/cli-linux-x64-musl@2.4.10': + optional: true + + '@biomejs/cli-linux-x64@2.4.10': + optional: true + + '@biomejs/cli-win32-arm64@2.4.10': + optional: true + + '@biomejs/cli-win32-x64@2.4.10': + optional: true + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + async@3.2.6: {} + + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + commander@14.0.3: {} + + corser@2.0.1: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + emoji-regex@10.6.0: {} + + environment@1.1.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.4: {} + + follow-redirects@1.15.11: {} + + function-bind@1.1.2: {} + + get-east-asian-width@1.5.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + gopd@1.2.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.11 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + http-server@14.1.1: + dependencies: + basic-auth: 2.0.1 + chalk: 4.1.2 + corser: 2.0.1 + he: 1.2.0 + html-encoding-sniffer: 3.0.0 + http-proxy: 1.18.1 + mime: 1.6.0 + minimist: 1.2.8 + opener: 1.5.2 + portfinder: 1.0.38 + secure-compare: 3.0.1 + union: 0.5.0 + url-join: 4.0.1 + transitivePeerDependencies: + - debug + - supports-color + + husky@9.1.7: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.5.0 + + lint-staged@16.4.0: + dependencies: + commander: 14.0.3 + listr2: 9.0.5 + picomatch: 4.0.4 + string-argv: 0.3.2 + tinyexec: 1.0.4 + yaml: 2.8.3 + + listr2@9.0.5: + dependencies: + cli-truncate: 5.2.0 + colorette: 2.0.20 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + math-intrinsics@1.1.0: {} + + mime@1.6.0: {} + + mimic-function@5.0.1: {} + + minimist@1.2.8: {} + + ms@2.1.3: {} + + object-inspect@1.13.4: {} + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + opener@1.5.2: {} + + picomatch@4.0.4: {} + + portfinder@1.0.38: + dependencies: + async: 3.2.6 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + qs@6.15.0: + dependencies: + side-channel: 1.1.0 + + requires-port@1.0.0: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + rfdc@1.4.1: {} + + safe-buffer@5.1.2: {} + + safer-buffer@2.1.2: {} + + secure-compare@3.0.1: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@4.1.0: {} + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + string-argv@0.3.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + string-width@8.2.0: + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tinyexec@1.0.4: {} + + union@0.5.0: + dependencies: + qs: 6.15.0 + + url-join@4.0.1: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + yaml@2.8.3: {} diff --git a/pom.xml b/pom.xml index 4c05de3..f0d4ef8 100644 --- a/pom.xml +++ b/pom.xml @@ -7,8 +7,7 @@ - SEMOSS JAR is "provided" (it's on the server classpath, not bundled) Build with: mvn clean compile (or use the SEMOSS UI build button) ---> - +--> 4.0.0 SemossTemplate SemossTemplate diff --git a/py/mcp_driver.py b/py/mcp_driver.py index 8c036c7..7dbce7d 100644 --- a/py/mcp_driver.py +++ b/py/mcp_driver.py @@ -10,22 +10,27 @@ from smssutil import mcp_metadata -@mcp_metadata({ - "execution": "auto", - "displayLocation": "inline", - "loadingMessage": "Converting temperature..." -}) + +@mcp_metadata( + { + "execution": "auto", + "displayLocation": "inline", + "loadingMessage": "Converting temperature...", + } +) def fahrenheit_to_celsius(temperature_f: float) -> str: """Convert a temperature from Fahrenheit to Celsius.""" celsius = (temperature_f - 32) * 5 / 9 return json.dumps({"fahrenheit": temperature_f, "celsius": round(celsius, 2)}) -@mcp_metadata({ - "execution": "auto", - "displayLocation": "inline", - "loadingMessage": "Converting temperature..." -}) +@mcp_metadata( + { + "execution": "auto", + "displayLocation": "inline", + "loadingMessage": "Converting temperature...", + } +) def celsius_to_fahrenheit(temperature_c: float) -> str: """Convert a temperature from Celsius to Fahrenheit.""" fahrenheit = temperature_c * 9 / 5 + 32 From a7dd94e162877c0550bfe371d52f330f76ce623c Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 3 Apr 2026 14:07:29 -0400 Subject: [PATCH 02/31] feat: add files --- .github/workflows/unit-test.yml | 67 ++++ biome.json | 9 +- client/package.json | 3 +- pom.xml | 104 +++++ test/README.md | 371 ++++++++++++++++++ test/reactors/BaseReactorTest.java | 273 +++++++++++++ test/reactors/ReactorTestSuite.java | 41 ++ .../example/CallPythonReactorTest.java | 197 ++++++++++ test/reactors/example/HelloReactorTest.java | 89 +++++ .../example/OpenMCPAppReactorTest.java | 108 +++++ 10 files changed, 1258 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/unit-test.yml create mode 100644 test/README.md create mode 100644 test/reactors/BaseReactorTest.java create mode 100644 test/reactors/ReactorTestSuite.java create mode 100644 test/reactors/example/CallPythonReactorTest.java create mode 100644 test/reactors/example/HelloReactorTest.java create mode 100644 test/reactors/example/OpenMCPAppReactorTest.java diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml new file mode 100644 index 0000000..f0f432b --- /dev/null +++ b/.github/workflows/unit-test.yml @@ -0,0 +1,67 @@ +name: Run Unit Tests + +on: + pull_request: + branches: [ default-app ] + workflow_dispatch: + inputs: + snapshot: + description: 'CI version snapshot' + required: true + default: '5.0.0-alpha-SNAPSHOT' + type: choice + options: + - '5.0.0-alpha-SNAPSHOT' + # Add other snapshot versions as needed here! + use_cache: + description: 'Use cached Maven dependencies if available' + required: true + default: true + type: boolean + clean_install: + description: 'Run `mvn clean install -U` if not using cache' + required: true + default: false + type: boolean + +jobs: + test: + name: Run Test Suite + runs-on: ubuntu-latest + container: maven:3.9.9-amazoncorretto-21-debian + env: + SNAPSHOT: ${{ inputs.snapshot || '5.0.0-alpha-SNAPSHOT' }} + USE_CACHE: ${{ inputs.use_cache || 'true' }} + CLEAN_INSTALL: ${{ inputs.clean_install || 'false' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Cache Maven packages + id: cache-maven + if: env.USE_CACHE == 'true' + uses: actions/cache@v4 + with: + path: /root/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Maven Install + if: env.USE_CACHE == 'false' || steps.cache-maven.outputs.cache-hit != 'true' + run: | + echo "Clean install: ${CLEAN_INSTALL}" + echo "Version snapshot: ${SNAPSHOT}" + if [ "${CLEAN_INSTALL}" = "true" ]; then + echo "Running mvn clean install with -U flag" + mvn clean install -U -DskipTests=true -Dci.version="${SNAPSHOT}" | grep -v "^Progress ([0-9]\+):" + else + echo "Running mvn install without -U flag" + mvn install -DskipTests=true -Dci.version="${SNAPSHOT}" | grep -v "^Progress ([0-9]\+):" + fi + + - name: Run Unit Tests + run: | + mvn test -Dci.version="${SNAPSHOT}" | grep -v "^Progress ([0-9]\+):" + \ No newline at end of file diff --git a/biome.json b/biome.json index 1a1663f..ad9ade3 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", + "$schema": "https://biomejs.dev/schemas/2.2.6/schema.json", "vcs": { "enabled": true, "clientKind": "git", @@ -45,8 +45,11 @@ } }, "css": { - "parser": { - "tailwindDirectives": true + "formatter": { + "enabled": true + }, + "linter": { + "enabled": true } }, "files": { diff --git a/client/package.json b/client/package.json index 516edfb..5fd0c1a 100644 --- a/client/package.json +++ b/client/package.json @@ -2,7 +2,8 @@ "private": true, "scripts": { "build": "vite build", - "dev": "vite" + "dev": "vite", + "javadoc": "mvn clean javadoc:javadoc && mvn site && pnpm exec http-server target/site/apidocs -p 1227 -a localhost -o" }, "dependencies": { "@radix-ui/react-label": "^2.1.8", diff --git a/pom.xml b/pom.xml index f0d4ef8..14bc844 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,9 @@ java/src + test classes + test-classes java @@ -35,6 +37,28 @@ 21 + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + 21 + 21 + UTF-8 + private + true + false + -Xdoclint:none + + + + attach-javadocs + + jar + + + + @@ -58,6 +82,37 @@ true + + public + Sonatype's Maven repository + https://oss.sonatype.org/content/groups/public + + true + + + false + + + + sonatype-snapshots + Sonatype Snapshots + https://central.sonatype.com/repository/maven-snapshots + + true + always + + + + 3rdPartyJARs + Maven repository + https://maven.semoss.org/repository/3rdPartyJARs + + true + + + true + + @@ -66,5 +121,54 @@ ${ci.version} provided + + + + org.junit.jupiter + junit-jupiter + 6.0.0 + test + + + + org.junit.platform + junit-platform-suite + 6.0.0 + test + + + + org.mockito + mockito-core + 5.18.0 + test + + + net.bytebuddy + byte-buddy + + + org.objenesis + objenesis + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + 21 + UTF-8 + private + true + false + -Xdoclint:none + + + + \ No newline at end of file diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..63dabaa --- /dev/null +++ b/test/README.md @@ -0,0 +1,371 @@ +# Reactor Test Suite + +Comprehensive test suite for SEMOSS Template project reactors with organized test structure, helper utilities, and extensive test coverage. + +## Test Structure + +``` +test/ +├── reactors/ +│ ├── BaseReactorTest.java # Base test class with common mocking utilities +│ ├── ReactorTestSuite.java # Test suite to run all tests together +│ └── example/ +│ ├── HelloReactorTest.java # Tests for HelloUserReactor +│ ├── CallPythonReactorTest.java # Tests for CallPythonReactor +│ └── OpenMCPAppReactorTest.java # Tests for OpenMCPAppReactor +``` + +## Running Tests + +### Run All Tests in the Suite + +```bash +mvn test -Dtest=ReactorTestSuite +``` + +### Run Individual Test Classes + +```bash +# Run HelloUserReactor tests +mvn test -Dtest=HelloReactorTest + +# Run CallPythonReactor tests +mvn test -Dtest=CallPythonReactorTest + +# Run OpenMCPAppReactor tests +mvn test -Dtest=OpenMCPAppReactorTest +``` + +### Run All Tests + +```bash +mvn test +``` + +### Run Specific Test Method + +```bash +mvn test -Dtest=HelloReactorTest#testHelloUserReactor_CustomName +``` + +## Testing Workflow + +### Development Cycle + +Testing should be an integral part of your reactor development process: + +1. **Design Phase** - Plan your reactor's functionality and identify test scenarios +2. **Implementation** - Write your reactor code in `java/src/reactors/` +3. **Test Creation** - Create corresponding test class in `test/reactors/` +4. **Validation** - Run tests to verify behavior +5. **Iteration** - Fix issues and re-run tests until all pass +6. **Integration** - Add test to suite and commit + +### Recommended Testing Workflow + +#### Option 1: Test-Driven Development (TDD) +Write tests before implementing the reactor: + +```bash +# 1. Create test class first +# test/reactors/example/YourReactorTest.java + +# 2. Run tests (they will fail) +mvn test -Dtest=YourReactorTest + +# 3. Implement reactor to make tests pass +# java/src/reactors/examples/YourReactor.java + +# 4. Run tests again +mvn test -Dtest=YourReactorTest + +# 5. Refactor and repeat until all tests pass +``` + +#### Option 2: Traditional Development +Write reactor first, then add tests: + +```bash +# 1. Implement reactor +# java/src/reactors/examples/YourReactor.java + +# 2. Create comprehensive tests +# test/reactors/example/YourReactorTest.java + +# 3. Run tests to verify +mvn test -Dtest=YourReactorTest + +# 4. Fix any issues discovered +``` + +### Pre-Commit Testing + +Always run tests before committing changes: + +```bash +# Run all tests +mvn test + +# Or run just the tests for modified reactors +mvn test -Dtest=YourModifiedReactorTest + +# Stage and commit only after tests pass +git add . +git commit -m "feat: Add YourReactor with comprehensive tests" +``` + +### Continuous Testing During Development + +For rapid feedback during active development: + +```bash +# Terminal 1: Keep this running +mvn test -Dtest=YourReactorTest + +# Terminal 2: Edit your code +# Make changes to reactor or test + +# Return to Terminal 1 and re-run after each change +``` + +### Integration with SEMOSS Development + +When working with the SEMOSS UI: + +1. **Before "Recompile reactors"** in SEMOSS UI: + ```bash + mvn test # Ensure tests pass + ``` + +2. **After compiling** in SEMOSS UI: + - Test reactor in the application + - If issues found, update tests to cover the bug + - Fix reactor code + - Re-run tests + +3. **Before "Publish files"**: + ```bash + mvn test # Final verification + ``` + +### Multi-Reactor Development + +When working on multiple reactors: + +```bash +# Run tests for specific package +mvn test -Dtest=reactors.example.*Test + +# Or run the full suite +mvn test -Dtest=ReactorTestSuite +``` + +### Debugging Failed Tests + +1. **Read the error message** - JUnit provides detailed failure information +2. **Check mock setup** - Verify mocks are configured correctly +3. **Add debug logging** - Use `System.out.println()` in tests temporarily +4. **Run in debug mode** - Use your IDE's debugger to step through +5. **Isolate the issue** - Run single test method to focus + +```bash +# Run single test method with verbose output +mvn test -Dtest=YourReactorTest#testSpecificScenario -X +``` + +### Workflow Best Practices + +✅ **Do:** +- Run tests frequently during development +- Write tests for bug fixes before fixing the bug +- Keep tests fast and focused +- Run full test suite before pushing to remote +- Update test documentation when adding new tests + +❌ **Don't:** +- Skip writing tests for "simple" reactors +- Commit code with failing tests +- Ignore test failures in CI/CD +- Write overly complex tests that are hard to maintain +- Test implementation details instead of behavior + +## Test Coverage + +### HelloUserReactor Tests +- ✅ Default user greeting (no parameters) +- ✅ Custom name parameter +- ✅ Empty string name parameter + +### CallPythonReactor Tests +- ✅ Fibonacci calculation for input 0 +- ✅ Fibonacci calculation for input 1 +- ✅ Fibonacci calculation for input 5 +- ✅ Fibonacci calculation for input 10 +- ✅ Fibonacci calculation for input 20 (large number) +- ✅ Argument list verification + +### OpenMCPAppReactor Tests +- ✅ Returns placeholder message +- ✅ Exact message verification +- ✅ No parameters required +- ✅ Reactor description verification +- ✅ Multiple executions consistency + +## BaseReactorTest Utilities + +The `BaseReactorTest` class provides common mocking utilities for all reactor tests: + +### Provided Mocks +- `@Mock Insight insight` - Mock insight for execution context +- `@Mock User user` - Mock user for authentication +- `@Mock NounStore nounStore` - Mock parameter storage +- `@Mock PyTranslator pyTranslator` - Mock Python integration +- `MockedStatic assetUtilsMock` - Mock asset utilities +- `Path tempDir` - Temporary directory for test files + +### Helper Methods + +#### Setting Reactor Parameters +```java +// Set string parameter +setReactorParameter(reactor, ReactorKeysEnum.NAME.getKey(), "Alice"); + +// Set numeric parameter +setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), 42); +``` + +#### Python Integration Setup +```java +// Mock Python module loading and function execution +setupPyTranslatorMocks("moduleName", "functionName", returnValue); +``` + +#### Creating Test Files +```java +// Create a Python file in the temp directory +createPythonFile("script.py", pythonCode); +``` + +#### Custom Project Properties +```java +@Override +protected void configureProjectProperties(Properties props) { + props.put("custom.property", "value"); +} +``` + +## Writing New Tests + +### Basic Test Structure + +```java +package reactors.example; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import reactors.BaseReactorTest; +import reactors.examples.YourReactor; +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("YourReactor Tests") +public class YourReactorTest extends BaseReactorTest { + + private YourReactor reactor; + + @BeforeEach + void setup() { + reactor = new YourReactor(); + reactor.setInsight(insight); + reactor.setNounStore(nounStore); + } + + @Test + @DisplayName("Description of what this test does") + public void testYourReactor_Scenario() { + // Arrange: Set up parameters + setReactorParameter(reactor, "paramKey", "paramValue"); + + // Act: Execute reactor + NounMetadata result = reactor.execute(); + + // Assert: Verify results + assertNotNull(result); + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + } +} +``` + +### Add to Test Suite + +Update `ReactorTestSuite.java` to include your new test class: + +```java +@SelectClasses({ + HelloReactorTest.class, + CallPythonReactorTest.class, + OpenMCPAppReactorTest.class, + YourNewReactorTest.class // Add here +}) +``` + +## Test Dependencies + +All required dependencies are already configured in `pom.xml`: + +- **JUnit Jupiter 6.0.0** - Testing framework +- **JUnit Platform Suite 6.0.0** - Test suite support +- **Mockito 5.18.0** - Mocking framework + +## Best Practices + +1. **Extend BaseReactorTest** - Always extend `BaseReactorTest` for new reactor tests +2. **Use @DisplayName** - Add descriptive display names to tests and test classes +3. **Arrange-Act-Assert** - Follow AAA pattern in test methods +4. **Test Multiple Scenarios** - Test happy path, edge cases, and error conditions +5. **Use Helper Methods** - Leverage `BaseReactorTest` helper methods for cleaner tests +6. **Mock External Dependencies** - Use provided mocks for PyTranslator, AssetUtility, etc. +7. **Verify Interactions** - Use Mockito's `verify()` to ensure proper method calls + +## Continuous Integration + +These tests are designed to run in CI/CD pipelines. Ensure your CI configuration includes: + +```yaml +# Example for GitHub Actions +- name: Run Tests + run: mvn test +``` + +## Troubleshooting + +### Tests Failing Due to Missing Dependencies +```bash +mvn clean install +``` + +### Cannot Find Test Classes +Ensure the test source directory is correctly configured in `pom.xml`: +```xml +test +``` + +### Mock Setup Issues +Verify that `MockitoAnnotations.openMocks(this)` is called in `BaseReactorTest.baseSetup()` + +### Python Integration Tests Failing +Ensure `setupPyTranslatorMocks()` is called with correct module and function names + +## Additional Resources + +- [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide/) +- [Mockito Documentation](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html) +- [SEMOSS Documentation](https://semoss.org/docs) + +## Contributing + +When adding new reactors, please: +1. Create corresponding test classes extending `BaseReactorTest` +2. Add comprehensive test coverage (minimum 3-5 test cases) +3. Update `ReactorTestSuite.java` to include new tests +4. Update this README with test coverage details diff --git a/test/reactors/BaseReactorTest.java b/test/reactors/BaseReactorTest.java new file mode 100644 index 0000000..3038ed6 --- /dev/null +++ b/test/reactors/BaseReactorTest.java @@ -0,0 +1,273 @@ +package reactors; + +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Properties; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import prerna.auth.AccessToken; +import prerna.auth.AuthProvider; +import prerna.auth.User; +import prerna.ds.py.PyTranslator; +import prerna.om.Insight; +import prerna.sablecc2.om.NounStore; +import prerna.util.AssetUtility; + +/** + * Base test class providing common mocking utilities and setup for reactor + * tests. + * All reactor test classes should extend this class to leverage shared test + * infrastructure. + * + *

+ * This class handles: + *

    + *
  • Mock setup and teardown for common SEMOSS components
  • + *
  • Temporary directory management for project assets
  • + *
  • User authentication mocking
  • + *
  • Project property file creation
  • + *
  • PyTranslator mocking for Python-based reactors
  • + *
+ */ +public abstract class BaseReactorTest { + + /** Mock insight providing context for reactor execution */ + @Mock + protected Insight insight; + + /** Mock user for authentication context */ + @Mock + protected User user; + + /** Mock noun store for reactor parameter management */ + @Mock + protected NounStore nounStore; + + /** Mock PyTranslator for Python integration testing */ + @Mock + protected PyTranslator pyTranslator; + + /** Static mock for AssetUtility to control project asset paths */ + protected MockedStatic assetUtilsMock; + + /** AutoCloseable for managing Mockito annotations lifecycle */ + private AutoCloseable mocks; + + /** Temporary directory for test execution */ + protected Path tempDir; + + /** Fake project ID for testing */ + protected static final String TEST_PROJECT_ID = "test-project-id"; + + /** Default test user name */ + protected static final String TEST_USER_NAME = "TestUser"; + + /** + * Sets up the test environment before each test execution. + * This method initializes mocks, creates temporary directories, and sets up + * common mock behaviors for insight, user, and asset utilities. + * + * @param p Temporary directory provided by JUnit + * @throws IOException if file operations fail during setup + */ + @BeforeEach + void baseSetup(@TempDir Path p) throws IOException { + tempDir = p; + mocks = MockitoAnnotations.openMocks(this); + + setupInsightMocks(); + setupUserMocks(); + setupAssetUtilityMocks(); + setupProjectProperties(); + } + + /** + * Sets up mock behaviors for the Insight object. + * Configures project ID retrieval and user context. + */ + protected void setupInsightMocks() { + when(insight.getUser()).thenReturn(user); + when(insight.getContextProjectId()).thenReturn(TEST_PROJECT_ID); + when(insight.getProjectId()).thenReturn(TEST_PROJECT_ID); + when(insight.getPyTranslator()).thenReturn(pyTranslator); + } + + /** + * Sets up mock behaviors for the User object. + * Creates a mock access token with test user credentials. + */ + protected void setupUserMocks() { + AccessToken token = new AccessToken(); + token.setName(TEST_USER_NAME); + token.setProvider(AuthProvider.NATIVE); + when(user.getPrimaryLoginToken()).thenReturn(token); + } + + /** + * Sets up static mocking for AssetUtility. + * Configures the asset utility to return the temporary directory as the project + * assets folder. + */ + protected void setupAssetUtilityMocks() { + assetUtilsMock = Mockito.mockStatic(AssetUtility.class); + assetUtilsMock.when(() -> AssetUtility.getProjectAssetsFolder(TEST_PROJECT_ID)) + .thenReturn(tempDir.toAbsolutePath().toString()); + } + + /** + * Creates a project.properties file in the temporary directory. + * Subclasses can override {@link #configureProjectProperties(Properties)} to + * add custom properties. + * + * @throws IOException if file creation or writing fails + */ + protected void setupProjectProperties() throws IOException { + Properties props = new Properties(); + configureProjectProperties(props); + + Path javaDir = tempDir.resolve("java"); + Files.createDirectories(javaDir); + Path projectPropertiesFile = javaDir.resolve("project.properties"); + + try (OutputStream os = Files.newOutputStream(projectPropertiesFile, + StandardOpenOption.WRITE, StandardOpenOption.CREATE)) { + props.store(os, "Test project properties"); + } + } + + /** + * Hook method for subclasses to configure custom project properties. + * Override this method to add specific properties needed for your reactor + * tests. + * + * @param props Properties object to configure + */ + protected void configureProjectProperties(Properties props) { + // Default implementation - subclasses can override to add custom properties + } + + /** + * Sets up mock behaviors for PyTranslator. + * Configures default responses for Python module loading and function + * execution. + * + * @param moduleName The name of the Python module to mock + * @param functionName The name of the Python function to mock + * @param returnValue The value to return when the function is called + */ + protected void setupPyTranslatorMocks(String moduleName, String functionName, Object returnValue) { + when(pyTranslator.loadPythonModuleFromFile( + Mockito.eq(insight), + Mockito.anyString(), + Mockito.eq(TEST_PROJECT_ID))) + .thenReturn(moduleName); + + when(pyTranslator.runFunctionFromLoadedModule( + Mockito.eq(insight), + Mockito.eq(moduleName), + Mockito.eq(functionName), + Mockito.anyList())) + .thenReturn(returnValue); + } + + /** + * Creates a Python source file in the temporary directory for testing. + * Useful for reactors that need to load Python files. + * + * @param fileName The name of the Python file to create + * @param content The content of the Python file + * @throws IOException if file creation fails + */ + protected void createPythonFile(String fileName, String content) throws IOException { + Path pyDir = tempDir.resolve("py"); + Files.createDirectories(pyDir); + Path pythonFile = pyDir.resolve(fileName); + Files.writeString(pythonFile, content); + } + + /** + * Helper method to set a parameter value on a reactor. + * This directly sets the value in the reactor's keyValue map via reflection. + * + * @param reactor The reactor to set the parameter on + * @param key The parameter key + * @param value The parameter value + */ + protected void setReactorParameter(reactors.AbstractProjectReactor reactor, String key, String value) { + try { + // Access the protected keyValue map field via reflection + java.lang.reflect.Field keyValueField = findField(reactor.getClass(), "keyValue"); + keyValueField.setAccessible(true); + + @SuppressWarnings("unchecked") + java.util.Map keyValueMap = (java.util.Map) keyValueField.get(reactor); + + // Initialize the map if it's null + if (keyValueMap == null) { + keyValueMap = new java.util.HashMap<>(); + keyValueField.set(reactor, keyValueMap); + } + + // Add the parameter to the map + keyValueMap.put(key, value); + + } catch (Exception e) { + throw new RuntimeException("Failed to set reactor parameter: " + key, e); + } + } + + /** + * Helper method to find a field in the class hierarchy. + */ + private java.lang.reflect.Field findField(Class clazz, String fieldName) throws NoSuchFieldException { + Class current = clazz; + while (current != null) { + try { + return current.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + current = current.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + /** + * Helper method to set a numeric parameter on a reactor. + * Converts the number to a string automatically. + * + * @param reactor The reactor to set the parameter on + * @param key The parameter key + * @param value The numeric value + */ + protected void setReactorParameter(reactors.AbstractProjectReactor reactor, String key, int value) { + setReactorParameter(reactor, key, String.valueOf(value)); + } + + /** + * Cleans up test resources after each test execution. + * Closes static mocks and Mockito annotations. + * + * @throws Exception if cleanup fails + */ + @AfterEach + void baseTearDown() throws Exception { + if (assetUtilsMock != null) { + assetUtilsMock.close(); + } + if (mocks != null) { + mocks.close(); + } + } +} diff --git a/test/reactors/ReactorTestSuite.java b/test/reactors/ReactorTestSuite.java new file mode 100644 index 0000000..9f12752 --- /dev/null +++ b/test/reactors/ReactorTestSuite.java @@ -0,0 +1,41 @@ +package reactors; + +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.api.SuiteDisplayName; + +import reactors.example.CallPythonReactorTest; +import reactors.example.HelloReactorTest; +import reactors.example.OpenMCPAppReactorTest; + +/** + * Test suite that runs all reactor tests in the project. + * This suite aggregates all reactor test classes and can be executed to run + * all tests at once for comprehensive validation. + * + *

+ * To run this suite: + * + *

+ * mvn test -Dtest=ReactorTestSuite
+ * 
+ * + *

+ * Included test classes: + *

    + *
  • {@link HelloReactorTest} - Tests for HelloUserReactor
  • + *
  • {@link CallPythonReactorTest} - Tests for CallPythonReactor
  • + *
  • {@link OpenMCPAppReactorTest} - Tests for OpenMCPAppReactor
  • + *
+ */ +@Suite +@SuiteDisplayName("Reactor Test Suite") +@SelectClasses({ + HelloReactorTest.class, + CallPythonReactorTest.class, + OpenMCPAppReactorTest.class +}) +public class ReactorTestSuite { + // This class remains empty, it is used only as a holder for the above + // annotations +} diff --git a/test/reactors/example/CallPythonReactorTest.java b/test/reactors/example/CallPythonReactorTest.java new file mode 100644 index 0000000..a27d704 --- /dev/null +++ b/test/reactors/example/CallPythonReactorTest.java @@ -0,0 +1,197 @@ +package reactors.example; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import reactors.BaseReactorTest; +import reactors.examples.CallPythonReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +/** + * Test class for CallPythonReactor functionality. + * Tests Python integration for Fibonacci number calculation. + */ +@DisplayName("CallPythonReactor Tests") +public class CallPythonReactorTest extends BaseReactorTest { + + private CallPythonReactor reactor; + private static final String FIBONACCI_MODULE = "nthFibonacci"; + private static final String FIBONACCI_FUNCTION = "nthFibonacci"; + + @BeforeEach + void setup() throws IOException { + reactor = new CallPythonReactor(); + reactor.setInsight(insight); + reactor.setNounStore(nounStore); + + // Copy the actual Python file from py directory to temp directory + Path sourcePath = Paths.get("py", "nthFibonacci.py"); + Path targetPath = tempDir.resolve("py").resolve("nthFibonacci.py"); + Files.createDirectories(targetPath.getParent()); + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); + } + + @Test + @DisplayName("Should calculate Fibonacci number for input 0") + public void testCallPythonReactor_Fibonacci0() { + // Set up mocks + int input = 0; + int expectedFibonacci = 0; + setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); + + // Set up reactor with input parameter + setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); + + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result type + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + + // Verify the result value + assertNotNull(result.getValue()); + assertEquals(expectedFibonacci, result.getValue()); + + // Verify Python translator was called correctly + verify(pyTranslator).loadPythonModuleFromFile( + eq(insight), + eq("nthFibonacci.py"), + eq(TEST_PROJECT_ID)); + verify(pyTranslator).runFunctionFromLoadedModule( + eq(insight), + eq(FIBONACCI_MODULE), + eq(FIBONACCI_FUNCTION), + anyList()); + } + + @Test + @DisplayName("Should calculate Fibonacci number for input 1") + public void testCallPythonReactor_Fibonacci1() { + // Set up mocks + int input = 1; + int expectedFibonacci = 1; + setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); + + // Set up reactor with input parameter + setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); + + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + assertEquals(expectedFibonacci, result.getValue()); + } + + @Test + @DisplayName("Should calculate Fibonacci number for input 5") + public void testCallPythonReactor_Fibonacci5() { + // Set up mocks + int input = 5; + int expectedFibonacci = 5; // Fibonacci(5) = 5 + setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); + + // Set up reactor with input parameter + setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); + + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + assertEquals(expectedFibonacci, result.getValue()); + } + + @Test + @DisplayName("Should calculate Fibonacci number for input 10") + public void testCallPythonReactor_Fibonacci10() { + // Set up mocks + int input = 10; + int expectedFibonacci = 55; // Fibonacci(10) = 55 + setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); + + // Set up reactor with input parameter + setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); + + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + assertEquals(expectedFibonacci, result.getValue()); + } + + @Test + @DisplayName("Should handle large Fibonacci numbers") + public void testCallPythonReactor_LargeFibonacci() { + // Set up mocks + int input = 20; + int expectedFibonacci = 6765; // Fibonacci(20) = 6765 + setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); + + // Set up reactor with input parameter + setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); + + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + assertEquals(expectedFibonacci, result.getValue()); + } + + @Test + @DisplayName("Should verify argument list passed to Python function") + public void testCallPythonReactor_VerifyArgumentList() { + // Set up mocks with argument capture + int input = 7; + int expectedFibonacci = 13; + + when(pyTranslator.loadPythonModuleFromFile( + eq(insight), + anyString(), + eq(TEST_PROJECT_ID))) + .thenReturn(FIBONACCI_MODULE); + + when(pyTranslator.runFunctionFromLoadedModule( + eq(insight), + eq(FIBONACCI_MODULE), + eq(FIBONACCI_FUNCTION), + anyList())) + .thenAnswer(invocation -> { + // Verify the argument list contains the correct input + @SuppressWarnings("unchecked") + List args = (List) invocation.getArgument(3); + assertEquals(1, args.size()); + assertEquals(input, args.get(0)); + return expectedFibonacci; + }); + + // Set up reactor with input parameter + setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); + + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result + assertEquals(expectedFibonacci, result.getValue()); + } +} diff --git a/test/reactors/example/HelloReactorTest.java b/test/reactors/example/HelloReactorTest.java new file mode 100644 index 0000000..7ef6193 --- /dev/null +++ b/test/reactors/example/HelloReactorTest.java @@ -0,0 +1,89 @@ +package reactors.example; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import reactors.BaseReactorTest; +import reactors.examples.HelloUserReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +/** + * Test class for HelloUserReactor functionality. + * Tests various scenarios including default user greeting and custom name + * parameter. + */ +@DisplayName("HelloUserReactor Tests") +public class HelloReactorTest extends BaseReactorTest { + + private HelloUserReactor reactor; + + @BeforeEach + void setup() { + reactor = new HelloUserReactor(); + reactor.setInsight(insight); + reactor.setNounStore(nounStore); + } + + @Test + @DisplayName("Should return greeting with default user name when no name parameter provided") + public void testHelloUserReactor_DefaultUserName() { + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result type + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + + // Verify the greeting contains the test user name + String greeting = (String) result.getValue(); + assertNotNull(greeting); + assertTrue(greeting.contains(TEST_USER_NAME)); + assertTrue(greeting.contains("Hello")); + assertTrue(greeting.contains("Welcome to SEMOSS")); + } + + @Test + @DisplayName("Should return greeting with custom name when name parameter provided") + public void testHelloUserReactor_CustomName() { + // Set up reactor with custom name parameter + String customName = "Alice"; + setReactorParameter(reactor, ReactorKeysEnum.NAME.getKey(), customName); + + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result type + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + + // Verify the greeting contains the custom name + String greeting = (String) result.getValue(); + assertNotNull(greeting); + assertTrue(greeting.contains(customName)); + assertTrue(greeting.contains("Hello")); + assertTrue(greeting.contains("Welcome to SEMOSS")); + } + + @Test + @DisplayName("Should handle empty string name parameter") + public void testHelloUserReactor_EmptyName() { + // Set up reactor with empty name parameter + setReactorParameter(reactor, ReactorKeysEnum.NAME.getKey(), ""); + + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result type + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + + // Verify the greeting is created (with empty name in this case) + String greeting = (String) result.getValue(); + assertNotNull(greeting); + assertTrue(greeting.contains("Hello")); + } +} diff --git a/test/reactors/example/OpenMCPAppReactorTest.java b/test/reactors/example/OpenMCPAppReactorTest.java new file mode 100644 index 0000000..325e679 --- /dev/null +++ b/test/reactors/example/OpenMCPAppReactorTest.java @@ -0,0 +1,108 @@ +package reactors.example; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import reactors.BaseReactorTest; +import reactors.examples.OpenMCPAppReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +/** + * Test class for OpenMCPAppReactor functionality. + * Tests the MCP App interface opening reactor. + */ +@DisplayName("OpenMCPAppReactor Tests") +public class OpenMCPAppReactorTest extends BaseReactorTest { + + private OpenMCPAppReactor reactor; + + @BeforeEach + void setup() { + reactor = new OpenMCPAppReactor(); + reactor.setInsight(insight); + reactor.setNounStore(nounStore); + } + + @Test + @DisplayName("Should return placeholder message indicating auto-execute not implemented") + public void testOpenMCPAppReactor_ReturnsPlaceholderMessage() { + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify result type + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + + // Verify the result contains expected message + String message = (String) result.getValue(); + assertNotNull(message); + assertTrue(message.contains("auto-execute response")); + assertTrue(message.contains("not yet been implemented")); + } + + @Test + @DisplayName("Should return exact expected placeholder message") + public void testOpenMCPAppReactor_ExactMessage() { + // Execute the reactor + NounMetadata result = reactor.execute(); + + // Verify exact message + String expectedMessage = "This MCP tool's auto-execute response has not yet been implemented."; + assertEquals(expectedMessage, result.getValue()); + } + + @Test + @DisplayName("Should not require any input parameters") + public void testOpenMCPAppReactor_NoParametersRequired() { + // Verify reactor can execute without any parameters set + NounMetadata result = reactor.execute(); + + // Should still return a valid result + assertNotNull(result); + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + } + + @Test + @DisplayName("Should return reactor description for MCP tool") + public void testOpenMCPAppReactor_Description() { + // Get reactor description + String description = reactor.getReactorDescription(); + + // Verify description is present and contains expected content + assertNotNull(description); + assertTrue(description.contains("SEMOSS Template application")); + assertTrue(description.contains("interact")); + } + + @Test + @DisplayName("Should return expected exact description") + public void testOpenMCPAppReactor_ExactDescription() { + // Get reactor description + String description = reactor.getReactorDescription(); + + // Verify exact description + String expectedDescription = "This tool allows the user to interact with the SEMOSS Template application."; + assertEquals(expectedDescription, description); + } + + @Test + @DisplayName("Should execute successfully multiple times") + public void testOpenMCPAppReactor_MultipleExecutions() { + // Execute the reactor multiple times + NounMetadata result1 = reactor.execute(); + NounMetadata result2 = reactor.execute(); + NounMetadata result3 = reactor.execute(); + + // All results should be identical + assertEquals(result1.getValue(), result2.getValue()); + assertEquals(result2.getValue(), result3.getValue()); + assertEquals(PixelDataType.CONST_STRING, result1.getNounType()); + assertEquals(PixelDataType.CONST_STRING, result2.getNounType()); + assertEquals(PixelDataType.CONST_STRING, result3.getNounType()); + } +} From 2489ddf7a970ab0752dec59fd3ab085c9ea0efa8 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 3 Apr 2026 14:13:11 -0400 Subject: [PATCH 03/31] fix: some more stuff here --- client/tsconfig.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/tsconfig.json b/client/tsconfig.json index 315550a..875302c 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "baseUrl": ".", "target": "es6", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, @@ -17,9 +16,10 @@ "incremental": true, "experimentalDecorators": true, "paths": { - "@/*": ["src/*"] + "@/*": ["./src/*"] }, - "outDir": "../portals" + "outDir": "../portals", + "rootDir": "./src" }, "include": ["./src/**/*"] } From 656b06a92dad71cd2fe35522a96e7c5d23d8e2eb Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 3 Apr 2026 14:28:30 -0400 Subject: [PATCH 04/31] fix: unit tests --- .github/workflows/unit-test.yml | 2 +- test/reactors/GetWeatherReactorTest.java | 100 +++++++++ test/reactors/ReactorTestSuite.java | 23 +- .../example/CallPythonReactorTest.java | 197 ------------------ test/reactors/example/HelloReactorTest.java | 89 -------- .../example/OpenMCPAppReactorTest.java | 108 ---------- 6 files changed, 103 insertions(+), 416 deletions(-) create mode 100644 test/reactors/GetWeatherReactorTest.java delete mode 100644 test/reactors/example/CallPythonReactorTest.java delete mode 100644 test/reactors/example/HelloReactorTest.java delete mode 100644 test/reactors/example/OpenMCPAppReactorTest.java diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index f0f432b..97396bf 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -2,7 +2,7 @@ name: Run Unit Tests on: pull_request: - branches: [ default-app ] + branches: [ example-dev ] workflow_dispatch: inputs: snapshot: diff --git a/test/reactors/GetWeatherReactorTest.java b/test/reactors/GetWeatherReactorTest.java new file mode 100644 index 0000000..2d427a2 --- /dev/null +++ b/test/reactors/GetWeatherReactorTest.java @@ -0,0 +1,100 @@ +package reactors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +@DisplayName("GetWeatherReactor Tests") +public class GetWeatherReactorTest extends BaseReactorTest { + + private GetWeatherReactor reactor; + + @BeforeEach + void setup() { + reactor = new GetWeatherReactor(); + reactor.setInsight(insight); + reactor.setNounStore(nounStore); + } + + @Test + @DisplayName("Should return sunny forecast for a given city") + void testGetWeather_validCity() { + setReactorParameter(reactor, "city", "Boston"); + + NounMetadata result = reactor.execute(); + + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + String forecast = (String) result.getValue(); + assertNotNull(forecast); + assertEquals("It will be sunny in Boston today.", forecast); + } + + @Test + @DisplayName("Should include city name in response for different cities") + void testGetWeather_differentCity() { + setReactorParameter(reactor, "city", "Tokyo"); + + NounMetadata result = reactor.execute(); + + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + String forecast = (String) result.getValue(); + assertTrue(forecast.contains("Tokyo")); + } + + @Test + @DisplayName("Should return error when city parameter is missing") + void testGetWeather_missingCity() { + // Don't set the city parameter — let it be null + NounMetadata result = reactor.execute(); + + // AbstractProjectReactor wraps exceptions as error responses + assertTrue( + result.getOpType().contains(PixelOperationType.ERROR) + || result.getValue().toString().toLowerCase().contains("null"), + "Expected an error or null-related message when city is missing" + ); + } + + @Test + @DisplayName("Should handle city with spaces") + void testGetWeather_cityWithSpaces() { + setReactorParameter(reactor, "city", "New York"); + + NounMetadata result = reactor.execute(); + + assertEquals(PixelDataType.CONST_STRING, result.getNounType()); + String forecast = (String) result.getValue(); + assertEquals("It will be sunny in New York today.", forecast); + } + + @Test + @DisplayName("Should return correct reactor description") + void testGetReactorDescription() { + String desc = reactor.getReactorDescription(); + assertNotNull(desc); + assertTrue(desc.toLowerCase().contains("weather")); + } + + @Test + @DisplayName("Should return correct description for city key") + void testGetDescriptionForKey_city() { + String desc = reactor.getDescriptionForKey("city"); + assertNotNull(desc); + assertTrue(desc.toLowerCase().contains("city")); + } + + @Test + @DisplayName("Should return null description for unknown key") + void testGetDescriptionForKey_unknown() { + String desc = reactor.getDescriptionForKey("nonexistent"); + assertEquals(null, desc); + } +} diff --git a/test/reactors/ReactorTestSuite.java b/test/reactors/ReactorTestSuite.java index 9f12752..29331bc 100644 --- a/test/reactors/ReactorTestSuite.java +++ b/test/reactors/ReactorTestSuite.java @@ -4,36 +4,17 @@ import org.junit.platform.suite.api.Suite; import org.junit.platform.suite.api.SuiteDisplayName; -import reactors.example.CallPythonReactorTest; -import reactors.example.HelloReactorTest; -import reactors.example.OpenMCPAppReactorTest; - /** * Test suite that runs all reactor tests in the project. - * This suite aggregates all reactor test classes and can be executed to run - * all tests at once for comprehensive validation. - * - *

- * To run this suite: - * + * *

  * mvn test -Dtest=ReactorTestSuite
  * 
- * - *

- * Included test classes: - *

    - *
  • {@link HelloReactorTest} - Tests for HelloUserReactor
  • - *
  • {@link CallPythonReactorTest} - Tests for CallPythonReactor
  • - *
  • {@link OpenMCPAppReactorTest} - Tests for OpenMCPAppReactor
  • - *
*/ @Suite @SuiteDisplayName("Reactor Test Suite") @SelectClasses({ - HelloReactorTest.class, - CallPythonReactorTest.class, - OpenMCPAppReactorTest.class + GetWeatherReactorTest.class }) public class ReactorTestSuite { // This class remains empty, it is used only as a holder for the above diff --git a/test/reactors/example/CallPythonReactorTest.java b/test/reactors/example/CallPythonReactorTest.java deleted file mode 100644 index a27d704..0000000 --- a/test/reactors/example/CallPythonReactorTest.java +++ /dev/null @@ -1,197 +0,0 @@ -package reactors.example; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import reactors.BaseReactorTest; -import reactors.examples.CallPythonReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Test class for CallPythonReactor functionality. - * Tests Python integration for Fibonacci number calculation. - */ -@DisplayName("CallPythonReactor Tests") -public class CallPythonReactorTest extends BaseReactorTest { - - private CallPythonReactor reactor; - private static final String FIBONACCI_MODULE = "nthFibonacci"; - private static final String FIBONACCI_FUNCTION = "nthFibonacci"; - - @BeforeEach - void setup() throws IOException { - reactor = new CallPythonReactor(); - reactor.setInsight(insight); - reactor.setNounStore(nounStore); - - // Copy the actual Python file from py directory to temp directory - Path sourcePath = Paths.get("py", "nthFibonacci.py"); - Path targetPath = tempDir.resolve("py").resolve("nthFibonacci.py"); - Files.createDirectories(targetPath.getParent()); - Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); - } - - @Test - @DisplayName("Should calculate Fibonacci number for input 0") - public void testCallPythonReactor_Fibonacci0() { - // Set up mocks - int input = 0; - int expectedFibonacci = 0; - setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); - - // Set up reactor with input parameter - setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); - - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result type - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - - // Verify the result value - assertNotNull(result.getValue()); - assertEquals(expectedFibonacci, result.getValue()); - - // Verify Python translator was called correctly - verify(pyTranslator).loadPythonModuleFromFile( - eq(insight), - eq("nthFibonacci.py"), - eq(TEST_PROJECT_ID)); - verify(pyTranslator).runFunctionFromLoadedModule( - eq(insight), - eq(FIBONACCI_MODULE), - eq(FIBONACCI_FUNCTION), - anyList()); - } - - @Test - @DisplayName("Should calculate Fibonacci number for input 1") - public void testCallPythonReactor_Fibonacci1() { - // Set up mocks - int input = 1; - int expectedFibonacci = 1; - setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); - - // Set up reactor with input parameter - setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); - - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - assertEquals(expectedFibonacci, result.getValue()); - } - - @Test - @DisplayName("Should calculate Fibonacci number for input 5") - public void testCallPythonReactor_Fibonacci5() { - // Set up mocks - int input = 5; - int expectedFibonacci = 5; // Fibonacci(5) = 5 - setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); - - // Set up reactor with input parameter - setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); - - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - assertEquals(expectedFibonacci, result.getValue()); - } - - @Test - @DisplayName("Should calculate Fibonacci number for input 10") - public void testCallPythonReactor_Fibonacci10() { - // Set up mocks - int input = 10; - int expectedFibonacci = 55; // Fibonacci(10) = 55 - setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); - - // Set up reactor with input parameter - setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); - - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - assertEquals(expectedFibonacci, result.getValue()); - } - - @Test - @DisplayName("Should handle large Fibonacci numbers") - public void testCallPythonReactor_LargeFibonacci() { - // Set up mocks - int input = 20; - int expectedFibonacci = 6765; // Fibonacci(20) = 6765 - setupPyTranslatorMocks(FIBONACCI_MODULE, FIBONACCI_FUNCTION, expectedFibonacci); - - // Set up reactor with input parameter - setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); - - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - assertEquals(expectedFibonacci, result.getValue()); - } - - @Test - @DisplayName("Should verify argument list passed to Python function") - public void testCallPythonReactor_VerifyArgumentList() { - // Set up mocks with argument capture - int input = 7; - int expectedFibonacci = 13; - - when(pyTranslator.loadPythonModuleFromFile( - eq(insight), - anyString(), - eq(TEST_PROJECT_ID))) - .thenReturn(FIBONACCI_MODULE); - - when(pyTranslator.runFunctionFromLoadedModule( - eq(insight), - eq(FIBONACCI_MODULE), - eq(FIBONACCI_FUNCTION), - anyList())) - .thenAnswer(invocation -> { - // Verify the argument list contains the correct input - @SuppressWarnings("unchecked") - List args = (List) invocation.getArgument(3); - assertEquals(1, args.size()); - assertEquals(input, args.get(0)); - return expectedFibonacci; - }); - - // Set up reactor with input parameter - setReactorParameter(reactor, ReactorKeysEnum.NUMERIC_VALUE.getKey(), input); - - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result - assertEquals(expectedFibonacci, result.getValue()); - } -} diff --git a/test/reactors/example/HelloReactorTest.java b/test/reactors/example/HelloReactorTest.java deleted file mode 100644 index 7ef6193..0000000 --- a/test/reactors/example/HelloReactorTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package reactors.example; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import reactors.BaseReactorTest; -import reactors.examples.HelloUserReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Test class for HelloUserReactor functionality. - * Tests various scenarios including default user greeting and custom name - * parameter. - */ -@DisplayName("HelloUserReactor Tests") -public class HelloReactorTest extends BaseReactorTest { - - private HelloUserReactor reactor; - - @BeforeEach - void setup() { - reactor = new HelloUserReactor(); - reactor.setInsight(insight); - reactor.setNounStore(nounStore); - } - - @Test - @DisplayName("Should return greeting with default user name when no name parameter provided") - public void testHelloUserReactor_DefaultUserName() { - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result type - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - - // Verify the greeting contains the test user name - String greeting = (String) result.getValue(); - assertNotNull(greeting); - assertTrue(greeting.contains(TEST_USER_NAME)); - assertTrue(greeting.contains("Hello")); - assertTrue(greeting.contains("Welcome to SEMOSS")); - } - - @Test - @DisplayName("Should return greeting with custom name when name parameter provided") - public void testHelloUserReactor_CustomName() { - // Set up reactor with custom name parameter - String customName = "Alice"; - setReactorParameter(reactor, ReactorKeysEnum.NAME.getKey(), customName); - - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result type - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - - // Verify the greeting contains the custom name - String greeting = (String) result.getValue(); - assertNotNull(greeting); - assertTrue(greeting.contains(customName)); - assertTrue(greeting.contains("Hello")); - assertTrue(greeting.contains("Welcome to SEMOSS")); - } - - @Test - @DisplayName("Should handle empty string name parameter") - public void testHelloUserReactor_EmptyName() { - // Set up reactor with empty name parameter - setReactorParameter(reactor, ReactorKeysEnum.NAME.getKey(), ""); - - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result type - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - - // Verify the greeting is created (with empty name in this case) - String greeting = (String) result.getValue(); - assertNotNull(greeting); - assertTrue(greeting.contains("Hello")); - } -} diff --git a/test/reactors/example/OpenMCPAppReactorTest.java b/test/reactors/example/OpenMCPAppReactorTest.java deleted file mode 100644 index 325e679..0000000 --- a/test/reactors/example/OpenMCPAppReactorTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package reactors.example; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import reactors.BaseReactorTest; -import reactors.examples.OpenMCPAppReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Test class for OpenMCPAppReactor functionality. - * Tests the MCP App interface opening reactor. - */ -@DisplayName("OpenMCPAppReactor Tests") -public class OpenMCPAppReactorTest extends BaseReactorTest { - - private OpenMCPAppReactor reactor; - - @BeforeEach - void setup() { - reactor = new OpenMCPAppReactor(); - reactor.setInsight(insight); - reactor.setNounStore(nounStore); - } - - @Test - @DisplayName("Should return placeholder message indicating auto-execute not implemented") - public void testOpenMCPAppReactor_ReturnsPlaceholderMessage() { - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify result type - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - - // Verify the result contains expected message - String message = (String) result.getValue(); - assertNotNull(message); - assertTrue(message.contains("auto-execute response")); - assertTrue(message.contains("not yet been implemented")); - } - - @Test - @DisplayName("Should return exact expected placeholder message") - public void testOpenMCPAppReactor_ExactMessage() { - // Execute the reactor - NounMetadata result = reactor.execute(); - - // Verify exact message - String expectedMessage = "This MCP tool's auto-execute response has not yet been implemented."; - assertEquals(expectedMessage, result.getValue()); - } - - @Test - @DisplayName("Should not require any input parameters") - public void testOpenMCPAppReactor_NoParametersRequired() { - // Verify reactor can execute without any parameters set - NounMetadata result = reactor.execute(); - - // Should still return a valid result - assertNotNull(result); - assertEquals(PixelDataType.CONST_STRING, result.getNounType()); - } - - @Test - @DisplayName("Should return reactor description for MCP tool") - public void testOpenMCPAppReactor_Description() { - // Get reactor description - String description = reactor.getReactorDescription(); - - // Verify description is present and contains expected content - assertNotNull(description); - assertTrue(description.contains("SEMOSS Template application")); - assertTrue(description.contains("interact")); - } - - @Test - @DisplayName("Should return expected exact description") - public void testOpenMCPAppReactor_ExactDescription() { - // Get reactor description - String description = reactor.getReactorDescription(); - - // Verify exact description - String expectedDescription = "This tool allows the user to interact with the SEMOSS Template application."; - assertEquals(expectedDescription, description); - } - - @Test - @DisplayName("Should execute successfully multiple times") - public void testOpenMCPAppReactor_MultipleExecutions() { - // Execute the reactor multiple times - NounMetadata result1 = reactor.execute(); - NounMetadata result2 = reactor.execute(); - NounMetadata result3 = reactor.execute(); - - // All results should be identical - assertEquals(result1.getValue(), result2.getValue()); - assertEquals(result2.getValue(), result3.getValue()); - assertEquals(PixelDataType.CONST_STRING, result1.getNounType()); - assertEquals(PixelDataType.CONST_STRING, result2.getNounType()); - assertEquals(PixelDataType.CONST_STRING, result3.getNounType()); - } -} From 0c2277dde403cbf0a829e7d3cc6242d9228c3025 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 3 Apr 2026 14:39:35 -0400 Subject: [PATCH 05/31] fix: nvm needed to pnpm i --- biome.json | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/biome.json b/biome.json index ad9ade3..1a1663f 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.2.6/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", "vcs": { "enabled": true, "clientKind": "git", @@ -45,11 +45,8 @@ } }, "css": { - "formatter": { - "enabled": true - }, - "linter": { - "enabled": true + "parser": { + "tailwindDirectives": true } }, "files": { From 082a5e2bd10dd2667b7d4f9c5ad0f1c4c69abd5e Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 3 Apr 2026 16:30:15 -0400 Subject: [PATCH 06/31] fix: fix --- client/package.json | 3 +- java/src/reactors/AbstractProjectReactor.java | 92 ++++++++++++------- java/src/reactors/GetWeatherReactor.java | 12 ++- java/src/util/Constants.java | 17 ++-- java/src/util/HelperMethods.java | 17 ++-- java/src/util/ProjectProperties.java | 47 +++++++--- package.json | 3 +- pom.xml | 11 --- 8 files changed, 124 insertions(+), 78 deletions(-) diff --git a/client/package.json b/client/package.json index 5fd0c1a..516edfb 100644 --- a/client/package.json +++ b/client/package.json @@ -2,8 +2,7 @@ "private": true, "scripts": { "build": "vite build", - "dev": "vite", - "javadoc": "mvn clean javadoc:javadoc && mvn site && pnpm exec http-server target/site/apidocs -p 1227 -a localhost -o" + "dev": "vite" }, "dependencies": { "@radix-ui/react-label": "^2.1.8", diff --git a/java/src/reactors/AbstractProjectReactor.java b/java/src/reactors/AbstractProjectReactor.java index 02cd038..2fc291a 100644 --- a/java/src/reactors/AbstractProjectReactor.java +++ b/java/src/reactors/AbstractProjectReactor.java @@ -12,40 +12,54 @@ import prerna.sablecc2.om.nounmeta.NounMetadata; import util.ProjectProperties; -// Base class for all reactors in this project. -// -// Every reactor you create should extend this class instead of AbstractReactor directly. -// It handles: -// - SEMOSS initialization (project ID, user context, project properties) -// - Standardized error handling (exceptions become error responses, not crashes) -// - Common helper methods (e.g. getMap for map-type parameters) -// -// To create a new reactor: -// 1. Create a new class in this folder extending AbstractProjectReactor -// 2. Define keysToGet (parameter names) and keyRequired (1=required, 0=optional) in the -// constructor -// 3. Implement doExecute() with your business logic -// 4. Access parameters via this.keyValue.get("paramName") after organizeKeys() runs -// 5. Return results as NounMetadata (strings, maps, etc.) -// -// See GetWeatherReactor.java for a working example. +/** + * Base class for all reactors in this project. + * + *

Every reactor you create should extend this class instead of {@link AbstractReactor} directly. + * It handles: + * + *

    + *
  • SEMOSS initialization (project ID, user context, project properties) + *
  • Standardized error handling (exceptions become error responses, not crashes) + *
  • Common helper methods (e.g. {@link #getMap(String)} for map-type parameters) + *
+ * + *

To create a new reactor: + * + *

    + *
  1. Create a new class in this folder extending {@code AbstractProjectReactor} + *
  2. Define {@code keysToGet} (parameter names) and {@code keyRequired} (1=required, 0=optional) + * in the constructor + *
  3. Implement {@link #doExecute()} with your business logic + *
  4. Access parameters via {@code this.keyValue.get("paramName")} after {@link #organizeKeys()} + * runs + *
  5. Return results as {@link NounMetadata} (strings, maps, etc.) + *
+ * + * @see GetWeatherReactor + */ public abstract class AbstractProjectReactor extends AbstractReactor { private static final Logger LOGGER = LogManager.getLogger(AbstractProjectReactor.class); - // These protected variables are available in all subclass reactors - protected User user; // The authenticated user running this reactor - protected String projectId; // The SEMOSS project/app ID - protected ProjectProperties projectProperties; // Values from java/project.properties + /** The authenticated user running this reactor. */ + protected User user; - // TODO: Initialize additional protected variables (engines, external services, - // etc.) + /** The SEMOSS project/app ID. */ + protected String projectId; + /** Values from {@code java/project.properties}. */ + protected ProjectProperties projectProperties; + + /** Stores the reactor result. */ protected NounMetadata result = null; - // Runs preExecute() for setup, then doExecute() for business logic. - // If anything throws, the error is logged and returned as an error response - // instead of crashing the reactor. + /** + * Runs {@link #preExecute()} for setup, then {@link #doExecute()} for business logic. If anything + * throws, the error is logged and returned as an error response instead of crashing the reactor. + * + * @return the result of reactor execution, or an error response if an exception occurs + */ @Override public NounMetadata execute() { try { @@ -57,9 +71,10 @@ public NounMetadata execute() { } } - // Sets up project context before your reactor logic runs. - // Override this to add your own initialization (e.g. loading engines), - // but always call super.preExecute() first. + /** + * Sets up project context before your reactor logic runs. Override this to add your own + * initialization (e.g. loading engines), but always call {@code super.preExecute()} first. + */ protected void preExecute() { // Resolve the project ID from the insight context projectId = this.insight.getContextProjectId(); @@ -77,9 +92,13 @@ protected void preExecute() { organizeKeys(); // Populates this.keyValue from the Pixel command arguments } - // Helper to extract a Map parameter from the Pixel command. - // Useful when the frontend passes JSON objects as parameters. - // Returns null if no map parameter is found. + /** + * Extracts a {@link Map} parameter from the Pixel command. Useful when the frontend passes JSON + * objects as parameters. + * + * @param paramName the name of the map parameter to extract + * @return the map value, or {@code null} if no map parameter is found + */ @SuppressWarnings("unchecked") protected Map getMap(String paramName) { GenRowStruct mapGrs = this.store.getGenRowStruct(paramName); @@ -98,8 +117,11 @@ protected Map getMap(String paramName) { return null; } - // Implement this in your reactor subclass. - // This is where your business logic goes. Access parameters via this.keyValue. - // Return your result wrapped in NounMetadata. + /** + * Implement this in your reactor subclass. This is where your business logic goes. Access + * parameters via {@code this.keyValue}. + * + * @return the result wrapped in {@link NounMetadata} + */ protected abstract NounMetadata doExecute(); } diff --git a/java/src/reactors/GetWeatherReactor.java b/java/src/reactors/GetWeatherReactor.java index 61fa20a..dc5d7dd 100644 --- a/java/src/reactors/GetWeatherReactor.java +++ b/java/src/reactors/GetWeatherReactor.java @@ -3,10 +3,14 @@ import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; -// GetWeather reactor: returns a hardcoded forecast for a given city. -// -// Called from the frontend as: GetWeather(city=["Boston"]) -// Note: SEMOSS strips the "Reactor" suffix, so GetWeatherReactor becomes GetWeather(). +/** + * GetWeather reactor: returns a hardcoded forecast for a given city. + * + *

Called from the frontend as: {@code GetWeather(city=["Boston"])} + * + *

Note: SEMOSS strips the "Reactor" suffix, so {@code GetWeatherReactor} becomes {@code + * GetWeather()}. + */ public class GetWeatherReactor extends AbstractProjectReactor { private static final String CITY_KEY = "city"; diff --git a/java/src/util/Constants.java b/java/src/util/Constants.java index 0a0c676..bc41aa2 100644 --- a/java/src/util/Constants.java +++ b/java/src/util/Constants.java @@ -1,11 +1,16 @@ package util; -// Shared constants used across reactors and utilities. -// Add static final fields here for values referenced in multiple places. -// -// Example: -// public static final String DEFAULT_MODEL = "gpt-4"; -// public static final int MAX_RETRIES = 3; +/** + * Shared constants used across reactors and utilities. Add {@code static final} fields here for + * values referenced in multiple places. + * + *

Example: + * + *

+ *   public static final String DEFAULT_MODEL = "gpt-4";
+ *   public static final int MAX_RETRIES = 3;
+ * 
+ */ public class Constants { // TODO: Add your project constants here diff --git a/java/src/util/HelperMethods.java b/java/src/util/HelperMethods.java index 6d70b1b..6142df9 100644 --- a/java/src/util/HelperMethods.java +++ b/java/src/util/HelperMethods.java @@ -1,9 +1,14 @@ package util; -// Shared utility methods used across reactors. -// Add static helper methods here for logic reused in multiple reactors. -// -// Example: -// public static String sanitizeInput(String input) { ... } -// public static Map buildSuccessResponse(Object data) { ... } +/** + * Shared utility methods used across reactors. Add static helper methods here for logic reused in + * multiple reactors. + * + *

Example: + * + *

+ *   public static String sanitizeInput(String input) { ... }
+ *   public static Map<String, Object> buildSuccessResponse(Object data) { ... }
+ * 
+ */ public class HelperMethods {} diff --git a/java/src/util/ProjectProperties.java b/java/src/util/ProjectProperties.java index b554520..367b8ed 100644 --- a/java/src/util/ProjectProperties.java +++ b/java/src/util/ProjectProperties.java @@ -8,15 +8,22 @@ import prerna.util.AssetUtility; import prerna.util.Utility; -// Loads configuration from java/project.properties and exposes values to reactors. -// -// This is a singleton: AbstractProjectReactor calls getInstance(projectId) during -// preExecute() to initialize it. After that, getInstance() returns the cached instance. -// -// To add a new property: -// 1. Add the key=value to java/project.properties -// 2. Add a private field and getter in this class -// 3. Read the value in loadProp() using projectProperties.getProperty("yourKey") +/** + * Loads configuration from {@code java/project.properties} and exposes values to reactors. + * + *

This is a singleton: {@link reactors.AbstractProjectReactor} calls {@link + * #getInstance(String)} during {@code preExecute()} to initialize it. After that, {@link + * #getInstance()} returns the cached instance. + * + *

To add a new property: + * + *

    + *
  1. Add the {@code key=value} to {@code java/project.properties} + *
  2. Add a private field and getter in this class + *
  3. Read the value in {@link #loadProp(String)} using {@code + * projectProperties.getProperty("yourKey")} + *
+ */ public class ProjectProperties { private static final Logger LOGGER = LogManager.getLogger(ProjectProperties.class); @@ -28,7 +35,12 @@ public class ProjectProperties { private ProjectProperties() {} - // Returns the cached singleton. Throws if getInstance(projectId) hasn't been called yet. + /** + * Returns the cached singleton. + * + * @return the singleton instance + * @throws RuntimeException if {@link #getInstance(String)} hasn't been called yet + */ public static ProjectProperties getInstance() { if (INSTANCE == null) { throw new RuntimeException("Unable to load project configuration"); @@ -36,7 +48,12 @@ public static ProjectProperties getInstance() { return INSTANCE; } - // First call: loads properties from disk. Subsequent calls: returns cached instance. + /** + * Returns the singleton, loading properties from disk on first call. + * + * @param projectId the SEMOSS project ID used to locate the properties file + * @return the singleton instance + */ public static ProjectProperties getInstance(String projectId) { if (INSTANCE == null) { loadProp(projectId); @@ -44,8 +61,12 @@ public static ProjectProperties getInstance(String projectId) { return INSTANCE; } - // Reads java/project.properties and populates this instance's fields. - // If the file is missing or unreadable, INSTANCE stays null and a warning is logged. + /** + * Reads {@code java/project.properties} and populates this instance's fields. If the file is + * missing or unreadable, {@code INSTANCE} stays {@code null} and a warning is logged. + * + * @param projectId the SEMOSS project ID used to locate the properties file + */ private static void loadProp(String projectId) { ProjectProperties newInstance = new ProjectProperties(); diff --git a/package.json b/package.json index eefd886..f2b5060 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "private": true, "scripts": { "fix": "biome format --write && biome lint && pre-commit run --all-files || pre-commit run --all-files", - "dev": "pnpm --dir client dev" + "dev": "pnpm --dir client dev", + "javadoc": "mvn clean javadoc:javadoc && mvn site && pnpm exec http-server target/site/apidocs -p 1227 -a localhost -o" }, "devDependencies": { "@biomejs/biome": "2.4.10", diff --git a/pom.xml b/pom.xml index 14bc844..82a1fae 100644 --- a/pom.xml +++ b/pom.xml @@ -102,17 +102,6 @@ always - - 3rdPartyJARs - Maven repository - https://maven.semoss.org/repository/3rdPartyJARs - - true - - - true - - From b60d1e3db0eba89436f301c41009a8da41862d85 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 3 Apr 2026 16:42:35 -0400 Subject: [PATCH 07/31] fix: this should fail --- .github/workflows/unit-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 97396bf..e764ab7 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -51,6 +51,7 @@ jobs: - name: Maven Install if: env.USE_CACHE == 'false' || steps.cache-maven.outputs.cache-hit != 'true' run: | + set -o pipefail echo "Clean install: ${CLEAN_INSTALL}" echo "Version snapshot: ${SNAPSHOT}" if [ "${CLEAN_INSTALL}" = "true" ]; then @@ -63,5 +64,6 @@ jobs: - name: Run Unit Tests run: | + set -o pipefail mvn test -Dci.version="${SNAPSHOT}" | grep -v "^Progress ([0-9]\+):" \ No newline at end of file From 64af3838f71acc97019c1722a72d8273db2d75ac Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 3 Apr 2026 16:52:52 -0400 Subject: [PATCH 08/31] fix: try this fix: try changing image fix: fix fix: fix fix: fix fix: fix --- .github/workflows/unit-test.yml | 29 ++++++++++------------------- pom.xml | 5 +++++ test/resources/log4j2-test.xml | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 test/resources/log4j2-test.xml diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index e764ab7..c2373a4 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -8,18 +8,13 @@ on: snapshot: description: 'CI version snapshot' required: true - default: '5.0.0-alpha-SNAPSHOT' + default: '5.2.0-SNAPSHOT' type: choice options: - - '5.0.0-alpha-SNAPSHOT' + - '5.2.0-SNAPSHOT' # Add other snapshot versions as needed here! - use_cache: - description: 'Use cached Maven dependencies if available' - required: true - default: true - type: boolean clean_install: - description: 'Run `mvn clean install -U` if not using cache' + description: 'Run `mvn clean install -U` to force-update dependencies' required: true default: false type: boolean @@ -30,8 +25,7 @@ jobs: runs-on: ubuntu-latest container: maven:3.9.9-amazoncorretto-21-debian env: - SNAPSHOT: ${{ inputs.snapshot || '5.0.0-alpha-SNAPSHOT' }} - USE_CACHE: ${{ inputs.use_cache || 'true' }} + SNAPSHOT: ${{ inputs.snapshot || '5.2.0-SNAPSHOT' }} CLEAN_INSTALL: ${{ inputs.clean_install || 'false' }} steps: @@ -40,30 +34,27 @@ jobs: - name: Cache Maven packages id: cache-maven - if: env.USE_CACHE == 'true' uses: actions/cache@v4 with: path: /root/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.SNAPSHOT }} restore-keys: | + ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}- ${{ runner.os }}-maven- - name: Maven Install - if: env.USE_CACHE == 'false' || steps.cache-maven.outputs.cache-hit != 'true' + if: steps.cache-maven.outputs.cache-hit != 'true' || env.CLEAN_INSTALL == 'true' run: | - set -o pipefail echo "Clean install: ${CLEAN_INSTALL}" echo "Version snapshot: ${SNAPSHOT}" if [ "${CLEAN_INSTALL}" = "true" ]; then echo "Running mvn clean install with -U flag" - mvn clean install -U -DskipTests=true -Dci.version="${SNAPSHOT}" | grep -v "^Progress ([0-9]\+):" + mvn -B clean install -U -DskipTests=true -Dci.version="${SNAPSHOT}" else echo "Running mvn install without -U flag" - mvn install -DskipTests=true -Dci.version="${SNAPSHOT}" | grep -v "^Progress ([0-9]\+):" + mvn -B install -DskipTests=true -Dci.version="${SNAPSHOT}" fi - name: Run Unit Tests - run: | - set -o pipefail - mvn test -Dci.version="${SNAPSHOT}" | grep -v "^Progress ([0-9]\+):" + run: mvn -B ${{ steps.cache-maven.outputs.cache-hit == 'true' && '-o' || '' }} test -Dci.version="${SNAPSHOT}" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 82a1fae..96034a8 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,11 @@ + + + test/resources + + maven-compiler-plugin diff --git a/test/resources/log4j2-test.xml b/test/resources/log4j2-test.xml new file mode 100644 index 0000000..d928d50 --- /dev/null +++ b/test/resources/log4j2-test.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file From 2722d90944cc841e187aaecc29aba235b482a1e6 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Tue, 7 Apr 2026 10:28:02 -0400 Subject: [PATCH 09/31] fix: test docs --- test/README.md | 48 +++++++++++++----------------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/test/README.md b/test/README.md index 63dabaa..3ac8715 100644 --- a/test/README.md +++ b/test/README.md @@ -8,11 +8,10 @@ Comprehensive test suite for SEMOSS Template project reactors with organized tes test/ ├── reactors/ │ ├── BaseReactorTest.java # Base test class with common mocking utilities -│ ├── ReactorTestSuite.java # Test suite to run all tests together -│ └── example/ -│ ├── HelloReactorTest.java # Tests for HelloUserReactor -│ ├── CallPythonReactorTest.java # Tests for CallPythonReactor -│ └── OpenMCPAppReactorTest.java # Tests for OpenMCPAppReactor +│ ├── GetWeatherReactorTest.java # Tests for GetWeatherReactor +│ └── ReactorTestSuite.java # Test suite to run all tests together +└── resources/ + └── log4j2-test.xml # Logging configuration for tests ``` ## Running Tests @@ -26,14 +25,8 @@ mvn test -Dtest=ReactorTestSuite ### Run Individual Test Classes ```bash -# Run HelloUserReactor tests -mvn test -Dtest=HelloReactorTest - -# Run CallPythonReactor tests -mvn test -Dtest=CallPythonReactorTest - -# Run OpenMCPAppReactor tests -mvn test -Dtest=OpenMCPAppReactorTest +# Run GetWeatherReactor tests +mvn test -Dtest=GetWeatherReactorTest ``` ### Run All Tests @@ -45,7 +38,7 @@ mvn test ### Run Specific Test Method ```bash -mvn test -Dtest=HelloReactorTest#testHelloUserReactor_CustomName +mvn test -Dtest=GetWeatherReactorTest#testGetWeatherReactor_CustomCity ``` ## Testing Workflow @@ -175,14 +168,14 @@ mvn test -Dtest=YourReactorTest#testSpecificScenario -X ### Workflow Best Practices -✅ **Do:** +**Do:** - Run tests frequently during development - Write tests for bug fixes before fixing the bug - Keep tests fast and focused - Run full test suite before pushing to remote - Update test documentation when adding new tests -❌ **Don't:** +**Don't:** - Skip writing tests for "simple" reactors - Commit code with failing tests - Ignore test failures in CI/CD @@ -191,25 +184,10 @@ mvn test -Dtest=YourReactorTest#testSpecificScenario -X ## Test Coverage -### HelloUserReactor Tests -- ✅ Default user greeting (no parameters) -- ✅ Custom name parameter -- ✅ Empty string name parameter - -### CallPythonReactor Tests -- ✅ Fibonacci calculation for input 0 -- ✅ Fibonacci calculation for input 1 -- ✅ Fibonacci calculation for input 5 -- ✅ Fibonacci calculation for input 10 -- ✅ Fibonacci calculation for input 20 (large number) -- ✅ Argument list verification - -### OpenMCPAppReactor Tests -- ✅ Returns placeholder message -- ✅ Exact message verification -- ✅ No parameters required -- ✅ Reactor description verification -- ✅ Multiple executions consistency +### GetWeatherReactor Tests +- Default weather retrieval +- Custom city parameter +- Error handling for missing parameters ## BaseReactorTest Utilities From 942683a17244c294f00ebace18d05f7f46d0de6f Mon Sep 17 00:00:00 2001 From: Van Buren Date: Tue, 7 Apr 2026 15:13:02 -0400 Subject: [PATCH 10/31] fix: tsconfig --- client/tsconfig.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/tsconfig.json b/client/tsconfig.json index 875302c..315550a 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": ".", "target": "es6", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, @@ -16,10 +17,9 @@ "incremental": true, "experimentalDecorators": true, "paths": { - "@/*": ["./src/*"] + "@/*": ["src/*"] }, - "outDir": "../portals", - "rootDir": "./src" + "outDir": "../portals" }, "include": ["./src/**/*"] } From 325fed68efcf0d3abbe3f9d4a820c198d9523831 Mon Sep 17 00:00:00 2001 From: Van Buren Date: Tue, 7 Apr 2026 15:18:35 -0400 Subject: [PATCH 11/31] feat: workflow on all PRs --- .github/workflows/pre-commit.yml | 1 - .github/workflows/unit-test.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index d1c53ca..43d5e2d 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -2,7 +2,6 @@ name: Pre-commit on: pull_request: - branches: [ default-app ] workflow_dispatch: jobs: diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index c2373a4..a6f454c 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -2,7 +2,6 @@ name: Run Unit Tests on: pull_request: - branches: [ example-dev ] workflow_dispatch: inputs: snapshot: From 4d146ddf33e329dc8edbc387f85a0c7e02b8e627 Mon Sep 17 00:00:00 2001 From: Van Buren Date: Tue, 7 Apr 2026 15:34:47 -0400 Subject: [PATCH 12/31] fix: upgrade lockfile --- pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6224996..1a1ac8a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -372,8 +372,8 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - tinyexec@1.0.4: - resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + tinyexec@1.1.1: + resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} engines: {node: '>=18'} union@0.5.0: @@ -597,7 +597,7 @@ snapshots: listr2: 9.0.5 picomatch: 4.0.4 string-argv: 0.3.2 - tinyexec: 1.0.4 + tinyexec: 1.1.1 yaml: 2.8.3 listr2@9.0.5: @@ -724,7 +724,7 @@ snapshots: dependencies: has-flag: 4.0.0 - tinyexec@1.0.4: {} + tinyexec@1.1.1: {} union@0.5.0: dependencies: From 6d59e4ea43ca4b04e534cbb9ceb2b09f09a16f25 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Wed, 8 Apr 2026 10:25:06 -0400 Subject: [PATCH 13/31] fix: remove javadoc --- java/src/reactors/AbstractProjectReactor.java | 22 ++++------ java/src/util/Constants.java | 5 +-- java/src/util/HelperMethods.java | 6 +-- java/src/util/ProjectProperties.java | 9 ++-- package.json | 3 +- pom.xml | 41 +------------------ test/reactors/BaseReactorTest.java | 15 +++---- test/reactors/GetWeatherReactorTest.java | 17 ++++++++ test/reactors/ReactorTestSuite.java | 4 +- 9 files changed, 40 insertions(+), 82 deletions(-) diff --git a/java/src/reactors/AbstractProjectReactor.java b/java/src/reactors/AbstractProjectReactor.java index 2fc291a..ffc4e3c 100644 --- a/java/src/reactors/AbstractProjectReactor.java +++ b/java/src/reactors/AbstractProjectReactor.java @@ -18,23 +18,17 @@ *

Every reactor you create should extend this class instead of {@link AbstractReactor} directly. * It handles: * - *

    - *
  • SEMOSS initialization (project ID, user context, project properties) - *
  • Standardized error handling (exceptions become error responses, not crashes) - *
  • Common helper methods (e.g. {@link #getMap(String)} for map-type parameters) - *
+ *

- SEMOSS initialization (project ID, user context, project properties) - Standardized error + * handling (exceptions become error responses, not crashes) - Common helper methods (e.g. {@link + * #getMap(String)} for map-type parameters) * *

To create a new reactor: * - *

    - *
  1. Create a new class in this folder extending {@code AbstractProjectReactor} - *
  2. Define {@code keysToGet} (parameter names) and {@code keyRequired} (1=required, 0=optional) - * in the constructor - *
  3. Implement {@link #doExecute()} with your business logic - *
  4. Access parameters via {@code this.keyValue.get("paramName")} after {@link #organizeKeys()} - * runs - *
  5. Return results as {@link NounMetadata} (strings, maps, etc.) - *
+ *

1. Create a new class in this folder extending {@code AbstractProjectReactor} 2. Define {@code + * keysToGet} (parameter names) and {@code keyRequired} (1=required, 0=optional) in the constructor + * 3. Implement {@link #doExecute()} with your business logic 4. Access parameters via {@code + * this.keyValue.get("paramName")} after {@link #organizeKeys()} runs 5. Return results as {@link + * NounMetadata} (strings, maps, etc.) * * @see GetWeatherReactor */ diff --git a/java/src/util/Constants.java b/java/src/util/Constants.java index bc41aa2..2b0adbb 100644 --- a/java/src/util/Constants.java +++ b/java/src/util/Constants.java @@ -6,10 +6,7 @@ * *

Example: * - *

- *   public static final String DEFAULT_MODEL = "gpt-4";
- *   public static final int MAX_RETRIES = 3;
- * 
+ *

public static final String DEFAULT_MODEL = "gpt-4"; public static final int MAX_RETRIES = 3; */ public class Constants { diff --git a/java/src/util/HelperMethods.java b/java/src/util/HelperMethods.java index 6142df9..7534542 100644 --- a/java/src/util/HelperMethods.java +++ b/java/src/util/HelperMethods.java @@ -6,9 +6,7 @@ * *

Example: * - *

- *   public static String sanitizeInput(String input) { ... }
- *   public static Map<String, Object> buildSuccessResponse(Object data) { ... }
- * 
+ *

public static String sanitizeInput(String input) { ... } public static Map + * buildSuccessResponse(Object data) { ... } */ public class HelperMethods {} diff --git a/java/src/util/ProjectProperties.java b/java/src/util/ProjectProperties.java index 367b8ed..6086755 100644 --- a/java/src/util/ProjectProperties.java +++ b/java/src/util/ProjectProperties.java @@ -17,12 +17,9 @@ * *

To add a new property: * - *

    - *
  1. Add the {@code key=value} to {@code java/project.properties} - *
  2. Add a private field and getter in this class - *
  3. Read the value in {@link #loadProp(String)} using {@code - * projectProperties.getProperty("yourKey")} - *
+ *

1. Add the {@code key=value} to {@code java/project.properties} 2. Add a private field and + * getter in this class 3. Read the value in {@link #loadProp(String)} using {@code + * projectProperties.getProperty("yourKey")} */ public class ProjectProperties { diff --git a/package.json b/package.json index f2b5060..eefd886 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,7 @@ "private": true, "scripts": { "fix": "biome format --write && biome lint && pre-commit run --all-files || pre-commit run --all-files", - "dev": "pnpm --dir client dev", - "javadoc": "mvn clean javadoc:javadoc && mvn site && pnpm exec http-server target/site/apidocs -p 1227 -a localhost -o" + "dev": "pnpm --dir client dev" }, "devDependencies": { "@biomejs/biome": "2.4.10", diff --git a/pom.xml b/pom.xml index 96034a8..efe3019 100644 --- a/pom.xml +++ b/pom.xml @@ -42,28 +42,6 @@ 21 - - org.apache.maven.plugins - maven-javadoc-plugin - 3.6.3 - - 21 - 21 - UTF-8 - private - true - false - -Xdoclint:none - - - - attach-javadocs - - jar - - - - @@ -115,7 +93,7 @@ ${ci.version} provided - + org.junit.jupiter @@ -148,21 +126,4 @@ - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.6.3 - - 21 - UTF-8 - private - true - false - -Xdoclint:none - - - - \ No newline at end of file diff --git a/test/reactors/BaseReactorTest.java b/test/reactors/BaseReactorTest.java index 3038ed6..a0c3211 100644 --- a/test/reactors/BaseReactorTest.java +++ b/test/reactors/BaseReactorTest.java @@ -30,16 +30,13 @@ * tests. * All reactor test classes should extend this class to leverage shared test * infrastructure. - * - *

+ * * This class handles: - *

    - *
  • Mock setup and teardown for common SEMOSS components
  • - *
  • Temporary directory management for project assets
  • - *
  • User authentication mocking
  • - *
  • Project property file creation
  • - *
  • PyTranslator mocking for Python-based reactors
  • - *
+ * - Mock setup and teardown for common SEMOSS components + * - Temporary directory management for project assets + * - User authentication mocking + * - Project property file creation + * - PyTranslator mocking for Python-based reactors */ public abstract class BaseReactorTest { diff --git a/test/reactors/GetWeatherReactorTest.java b/test/reactors/GetWeatherReactorTest.java index 2d427a2..6b2abd9 100644 --- a/test/reactors/GetWeatherReactorTest.java +++ b/test/reactors/GetWeatherReactorTest.java @@ -12,11 +12,18 @@ import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Tests for {@link GetWeatherReactor}. + * + * Verifies the reactor's output for valid cities, missing parameters, + * cities with spaces, and reactor/key description methods. + */ @DisplayName("GetWeatherReactor Tests") public class GetWeatherReactorTest extends BaseReactorTest { private GetWeatherReactor reactor; + /** Initializes a fresh {@link GetWeatherReactor} before each test. */ @BeforeEach void setup() { reactor = new GetWeatherReactor(); @@ -24,6 +31,7 @@ void setup() { reactor.setNounStore(nounStore); } + /** Verifies that a valid city returns the expected sunny forecast string. */ @Test @DisplayName("Should return sunny forecast for a given city") void testGetWeather_validCity() { @@ -37,6 +45,7 @@ void testGetWeather_validCity() { assertEquals("It will be sunny in Boston today.", forecast); } + /** Verifies that the city name is included in the response for any city. */ @Test @DisplayName("Should include city name in response for different cities") void testGetWeather_differentCity() { @@ -49,6 +58,10 @@ void testGetWeather_differentCity() { assertTrue(forecast.contains("Tokyo")); } + /** + * Verifies that omitting the city parameter results in an error response. + * {@link AbstractProjectReactor} wraps unhandled exceptions as error responses. + */ @Test @DisplayName("Should return error when city parameter is missing") void testGetWeather_missingCity() { @@ -63,6 +76,7 @@ void testGetWeather_missingCity() { ); } + /** Verifies that city names containing spaces are handled correctly. */ @Test @DisplayName("Should handle city with spaces") void testGetWeather_cityWithSpaces() { @@ -75,6 +89,7 @@ void testGetWeather_cityWithSpaces() { assertEquals("It will be sunny in New York today.", forecast); } + /** Verifies that {@code getReactorDescription()} returns a non-null weather-related string. */ @Test @DisplayName("Should return correct reactor description") void testGetReactorDescription() { @@ -83,6 +98,7 @@ void testGetReactorDescription() { assertTrue(desc.toLowerCase().contains("weather")); } + /** Verifies that {@code getDescriptionForKey("city")} returns a city-related description. */ @Test @DisplayName("Should return correct description for city key") void testGetDescriptionForKey_city() { @@ -91,6 +107,7 @@ void testGetDescriptionForKey_city() { assertTrue(desc.toLowerCase().contains("city")); } + /** Verifies that {@code getDescriptionForKey()} returns {@code null} for unknown keys. */ @Test @DisplayName("Should return null description for unknown key") void testGetDescriptionForKey_unknown() { diff --git a/test/reactors/ReactorTestSuite.java b/test/reactors/ReactorTestSuite.java index 29331bc..0096c29 100644 --- a/test/reactors/ReactorTestSuite.java +++ b/test/reactors/ReactorTestSuite.java @@ -7,9 +7,7 @@ /** * Test suite that runs all reactor tests in the project. * - *
- * mvn test -Dtest=ReactorTestSuite
- * 
+ * mvn test -Dtest=ReactorTestSuite */ @Suite @SuiteDisplayName("Reactor Test Suite") From 3578f171b20b7527a02a54ada123c44cd29d384b Mon Sep 17 00:00:00 2001 From: Van Buren Date: Mon, 13 Apr 2026 14:19:55 -0400 Subject: [PATCH 14/31] feat: up --- biome.json | 2 +- client/package.json | 5 +- client/pnpm-lock.yaml | 2 +- mcp/py_mcp.json | 130 ++++++++++++++++++++---------------------- package.json | 2 +- pnpm-lock.yaml | 100 ++++++++++++++++---------------- 6 files changed, 119 insertions(+), 122 deletions(-) diff --git a/biome.json b/biome.json index 1a1663f..ae19203 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.11/schema.json", "vcs": { "enabled": true, "clientKind": "git", diff --git a/client/package.json b/client/package.json index dd88a77..bc782bb 100644 --- a/client/package.json +++ b/client/package.json @@ -2,12 +2,13 @@ "private": true, "scripts": { "build": "vite build", - "dev": "vite" + "dev": "vite", + "fix": "pnpm --dir .. fix" }, "dependencies": { "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-slot": "^1.2.4", - "@semoss/sdk": "1.0.0-beta.38", + "@semoss/sdk": "^1.0.0-beta.38", "@tailwindcss/postcss": "^4.2.2", "@tailwindcss/vite": "^4.2.2", "autoprefixer": "^10.4.27", diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index 9d4d858..526f215 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -15,7 +15,7 @@ importers: specifier: ^1.2.4 version: 1.2.4(@types/react@18.3.28)(react@18.3.1) '@semoss/sdk': - specifier: 1.0.0-beta.38 + specifier: ^1.0.0-beta.38 version: 1.0.0-beta.38(react@18.3.1) '@tailwindcss/postcss': specifier: ^4.2.2 diff --git a/mcp/py_mcp.json b/mcp/py_mcp.json index 6cc9206..4bbf814 100644 --- a/mcp/py_mcp.json +++ b/mcp/py_mcp.json @@ -1,68 +1,64 @@ { - "_meta": { - "last_modified_date": "2026-04-10", - "file_last_modified_date": "2026-04-10", - "source_file": "/Users/tevanburen/Documents/SEMOSS/workspace/Semoss/project/Blank Canvas__9447b875-266e-4814-a1de-c840ff88401a/app_root/version/assets/py/mcp_driver.py" - }, - "tools": [ - { - "name": "fahrenheit_to_celsius", - "title": "Fahrenheit To Celsius", - "description": "Convert a temperature from Fahrenheit to Celsius.", - "inputSchema": { - "properties": { - "temperature_f": { - "title": "Temperature F", - "description": "No docstring present or unable to parse docstring from function", - "type": "number" - } - }, - "required": [ - "temperature_f" - ], - "title": "Fahrenheit To Celsius Arguments", - "type": "object" - }, - "_meta": { - "generated_on": "2026-04-10", - "SMSS_MCP_EXECUTION": "auto", - "SMSS_MCP_UI": { - "displayLocation": "inline", - "loadingMessage": "Converting temperature...", - "resourceURI": "/#/" - }, - "SMSS_FUNCTION_NAME": "fahrenheit_to_celsius" - }, - "_type": "python" - }, - { - "name": "celsius_to_fahrenheit", - "title": "Celsius To Fahrenheit", - "description": "Convert a temperature from Celsius to Fahrenheit.", - "inputSchema": { - "properties": { - "temperature_c": { - "title": "Temperature C", - "description": "No docstring present or unable to parse docstring from function", - "type": "number" - } - }, - "required": [ - "temperature_c" - ], - "title": "Celsius To Fahrenheit Arguments", - "type": "object" - }, - "_meta": { - "generated_on": "2026-04-10", - "SMSS_MCP_EXECUTION": "auto", - "SMSS_MCP_UI": { - "displayLocation": "inline", - "loadingMessage": "Converting temperature..." - }, - "SMSS_FUNCTION_NAME": "celsius_to_fahrenheit" - }, - "_type": "python" - } - ] -} \ No newline at end of file + "_meta": { + "last_modified_date": "2026-04-10", + "file_last_modified_date": "2026-04-10", + "source_file": "/Users/tevanburen/Documents/SEMOSS/workspace/Semoss/project/Blank Canvas__9447b875-266e-4814-a1de-c840ff88401a/app_root/version/assets/py/mcp_driver.py" + }, + "tools": [ + { + "name": "fahrenheit_to_celsius", + "title": "Fahrenheit To Celsius", + "description": "Convert a temperature from Fahrenheit to Celsius.", + "inputSchema": { + "properties": { + "temperature_f": { + "title": "Temperature F", + "description": "No docstring present or unable to parse docstring from function", + "type": "number" + } + }, + "required": ["temperature_f"], + "title": "Fahrenheit To Celsius Arguments", + "type": "object" + }, + "_meta": { + "generated_on": "2026-04-10", + "SMSS_MCP_EXECUTION": "auto", + "SMSS_MCP_UI": { + "displayLocation": "inline", + "loadingMessage": "Converting temperature...", + "resourceURI": "/#/" + }, + "SMSS_FUNCTION_NAME": "fahrenheit_to_celsius" + }, + "_type": "python" + }, + { + "name": "celsius_to_fahrenheit", + "title": "Celsius To Fahrenheit", + "description": "Convert a temperature from Celsius to Fahrenheit.", + "inputSchema": { + "properties": { + "temperature_c": { + "title": "Temperature C", + "description": "No docstring present or unable to parse docstring from function", + "type": "number" + } + }, + "required": ["temperature_c"], + "title": "Celsius To Fahrenheit Arguments", + "type": "object" + }, + "_meta": { + "generated_on": "2026-04-10", + "SMSS_MCP_EXECUTION": "auto", + "SMSS_MCP_UI": { + "displayLocation": "inline", + "loadingMessage": "Converting temperature..." + }, + "SMSS_FUNCTION_NAME": "celsius_to_fahrenheit" + }, + "_type": "python" + } + ] +} diff --git a/package.json b/package.json index eefd886..8e9638f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "dev": "pnpm --dir client dev" }, "devDependencies": { - "@biomejs/biome": "2.4.10", + "@biomejs/biome": "2.4.11", "http-server": "^14.1.1", "husky": "^9.1.7", "lint-staged": "^16.4.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a1ac8a..561f5de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: devDependencies: '@biomejs/biome': - specifier: 2.4.10 - version: 2.4.10 + specifier: 2.4.11 + version: 2.4.11 http-server: specifier: ^14.1.1 version: 14.1.1 @@ -23,59 +23,59 @@ importers: packages: - '@biomejs/biome@2.4.10': - resolution: {integrity: sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==} + '@biomejs/biome@2.4.11': + resolution: {integrity: sha512-nWxHX8tf3Opb/qRgZpBbsTOqOodkbrkJ7S+JxJAruxOReaDPPmPuLBAGQ8vigyUgo0QBB+oQltNEAvalLcjggA==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.4.10': - resolution: {integrity: sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==} + '@biomejs/cli-darwin-arm64@2.4.11': + resolution: {integrity: sha512-wOt+ed+L2dgZanWyL6i29qlXMc088N11optzpo10peayObBaAshbTcxKUchzEMp9QSY8rh5h6VfAFE3WTS1rqg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.4.10': - resolution: {integrity: sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==} + '@biomejs/cli-darwin-x64@2.4.11': + resolution: {integrity: sha512-gZ6zR8XmZlExfi/Pz/PffmdpWOQ8Qhy7oBztgkR8/ylSRyLwfRPSadmiVCV8WQ8PoJ2MWUy2fgID9zmtgUUJmw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.4.10': - resolution: {integrity: sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==} + '@biomejs/cli-linux-arm64-musl@2.4.11': + resolution: {integrity: sha512-+Sbo1OAmlegtdwqFE8iOxFIWLh1B3OEgsuZfBpyyN/kWuqZ8dx9ZEes6zVnDMo+zRHF2wLynRVhoQmV7ohxl2Q==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [musl] - '@biomejs/cli-linux-arm64@2.4.10': - resolution: {integrity: sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==} + '@biomejs/cli-linux-arm64@2.4.11': + resolution: {integrity: sha512-avdJaEElXrKceK0va9FkJ4P5ci3N01TGkc6ni3P8l3BElqbOz42Wg2IyX3gbh0ZLEd4HVKEIrmuVu/AMuSeFFA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [glibc] - '@biomejs/cli-linux-x64-musl@2.4.10': - resolution: {integrity: sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==} + '@biomejs/cli-linux-x64-musl@2.4.11': + resolution: {integrity: sha512-bexd2IklK7ZgPhrz6jXzpIL6dEAH9MlJU1xGTrypx+FICxrXUp4CqtwfiuoDKse+UlgAlWtzML3jrMqeEAHEhA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [musl] - '@biomejs/cli-linux-x64@2.4.10': - resolution: {integrity: sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==} + '@biomejs/cli-linux-x64@2.4.11': + resolution: {integrity: sha512-TagWV0iomp5LnEnxWFg4nQO+e52Fow349vaX0Q/PIcX6Zhk4GGBgp3qqZ8PVkpC+cuehRctMf3+6+FgQ8jCEFQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [glibc] - '@biomejs/cli-win32-arm64@2.4.10': - resolution: {integrity: sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==} + '@biomejs/cli-win32-arm64@2.4.11': + resolution: {integrity: sha512-RJhaTnY8byzxDt4bDVb7AFPHkPcjOPK3xBip4ZRTrN3TEfyhjLRm3r3mqknqydgVTB74XG8l4jMLwEACEeihVg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.4.10': - resolution: {integrity: sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==} + '@biomejs/cli-win32-x64@2.4.11': + resolution: {integrity: sha512-A8D3JM/00C2KQgUV3oj8Ba15EHEYwebAGCy5Sf9GAjr5Y3+kJIYOiESoqRDeuRZueuMdCsbLZIUqmPhpYXJE9A==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -179,8 +179,8 @@ packages: eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -301,8 +301,8 @@ packages: resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==} engines: {node: '>= 10.12'} - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} engines: {node: '>=0.6'} requires-port@1.0.0: @@ -324,8 +324,8 @@ packages: secure-compare@3.0.1: resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} side-channel-map@1.0.1: @@ -399,39 +399,39 @@ packages: snapshots: - '@biomejs/biome@2.4.10': + '@biomejs/biome@2.4.11': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.4.10 - '@biomejs/cli-darwin-x64': 2.4.10 - '@biomejs/cli-linux-arm64': 2.4.10 - '@biomejs/cli-linux-arm64-musl': 2.4.10 - '@biomejs/cli-linux-x64': 2.4.10 - '@biomejs/cli-linux-x64-musl': 2.4.10 - '@biomejs/cli-win32-arm64': 2.4.10 - '@biomejs/cli-win32-x64': 2.4.10 - - '@biomejs/cli-darwin-arm64@2.4.10': + '@biomejs/cli-darwin-arm64': 2.4.11 + '@biomejs/cli-darwin-x64': 2.4.11 + '@biomejs/cli-linux-arm64': 2.4.11 + '@biomejs/cli-linux-arm64-musl': 2.4.11 + '@biomejs/cli-linux-x64': 2.4.11 + '@biomejs/cli-linux-x64-musl': 2.4.11 + '@biomejs/cli-win32-arm64': 2.4.11 + '@biomejs/cli-win32-x64': 2.4.11 + + '@biomejs/cli-darwin-arm64@2.4.11': optional: true - '@biomejs/cli-darwin-x64@2.4.10': + '@biomejs/cli-darwin-x64@2.4.11': optional: true - '@biomejs/cli-linux-arm64-musl@2.4.10': + '@biomejs/cli-linux-arm64-musl@2.4.11': optional: true - '@biomejs/cli-linux-arm64@2.4.10': + '@biomejs/cli-linux-arm64@2.4.11': optional: true - '@biomejs/cli-linux-x64-musl@2.4.10': + '@biomejs/cli-linux-x64-musl@2.4.11': optional: true - '@biomejs/cli-linux-x64@2.4.10': + '@biomejs/cli-linux-x64@2.4.11': optional: true - '@biomejs/cli-win32-arm64@2.4.10': + '@biomejs/cli-win32-arm64@2.4.11': optional: true - '@biomejs/cli-win32-x64@2.4.10': + '@biomejs/cli-win32-x64@2.4.11': optional: true ansi-escapes@7.3.0: @@ -514,7 +514,7 @@ snapshots: eventemitter3@5.0.4: {} - follow-redirects@1.15.11: {} + follow-redirects@1.16.0: {} function-bind@1.1.2: {} @@ -557,7 +557,7 @@ snapshots: http-proxy@1.18.1: dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.11 + follow-redirects: 1.16.0 requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -644,7 +644,7 @@ snapshots: transitivePeerDependencies: - supports-color - qs@6.15.0: + qs@6.15.1: dependencies: side-channel: 1.1.0 @@ -663,7 +663,7 @@ snapshots: secure-compare@3.0.1: {} - side-channel-list@1.0.0: + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -687,7 +687,7 @@ snapshots: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 - side-channel-list: 1.0.0 + side-channel-list: 1.0.1 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 @@ -728,7 +728,7 @@ snapshots: union@0.5.0: dependencies: - qs: 6.15.0 + qs: 6.15.1 url-join@4.0.1: {} From 74b3a6f57ce882e723bccef2306191a7471029ba Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Mon, 13 Apr 2026 14:25:20 -0400 Subject: [PATCH 15/31] docs: changing comments --- java/src/reactors/AbstractProjectReactor.java | 86 +++++----- java/src/reactors/GetWeatherReactor.java | 12 +- java/src/util/Constants.java | 14 +- java/src/util/HelperMethods.java | 15 +- java/src/util/ProjectProperties.java | 44 ++---- test/reactors/BaseReactorTest.java | 147 ++++++------------ test/reactors/GetWeatherReactorTest.java | 30 ++-- test/reactors/ReactorTestSuite.java | 8 +- 8 files changed, 128 insertions(+), 228 deletions(-) diff --git a/java/src/reactors/AbstractProjectReactor.java b/java/src/reactors/AbstractProjectReactor.java index ffc4e3c..02cd038 100644 --- a/java/src/reactors/AbstractProjectReactor.java +++ b/java/src/reactors/AbstractProjectReactor.java @@ -12,48 +12,40 @@ import prerna.sablecc2.om.nounmeta.NounMetadata; import util.ProjectProperties; -/** - * Base class for all reactors in this project. - * - *

Every reactor you create should extend this class instead of {@link AbstractReactor} directly. - * It handles: - * - *

- SEMOSS initialization (project ID, user context, project properties) - Standardized error - * handling (exceptions become error responses, not crashes) - Common helper methods (e.g. {@link - * #getMap(String)} for map-type parameters) - * - *

To create a new reactor: - * - *

1. Create a new class in this folder extending {@code AbstractProjectReactor} 2. Define {@code - * keysToGet} (parameter names) and {@code keyRequired} (1=required, 0=optional) in the constructor - * 3. Implement {@link #doExecute()} with your business logic 4. Access parameters via {@code - * this.keyValue.get("paramName")} after {@link #organizeKeys()} runs 5. Return results as {@link - * NounMetadata} (strings, maps, etc.) - * - * @see GetWeatherReactor - */ +// Base class for all reactors in this project. +// +// Every reactor you create should extend this class instead of AbstractReactor directly. +// It handles: +// - SEMOSS initialization (project ID, user context, project properties) +// - Standardized error handling (exceptions become error responses, not crashes) +// - Common helper methods (e.g. getMap for map-type parameters) +// +// To create a new reactor: +// 1. Create a new class in this folder extending AbstractProjectReactor +// 2. Define keysToGet (parameter names) and keyRequired (1=required, 0=optional) in the +// constructor +// 3. Implement doExecute() with your business logic +// 4. Access parameters via this.keyValue.get("paramName") after organizeKeys() runs +// 5. Return results as NounMetadata (strings, maps, etc.) +// +// See GetWeatherReactor.java for a working example. public abstract class AbstractProjectReactor extends AbstractReactor { private static final Logger LOGGER = LogManager.getLogger(AbstractProjectReactor.class); - /** The authenticated user running this reactor. */ - protected User user; + // These protected variables are available in all subclass reactors + protected User user; // The authenticated user running this reactor + protected String projectId; // The SEMOSS project/app ID + protected ProjectProperties projectProperties; // Values from java/project.properties - /** The SEMOSS project/app ID. */ - protected String projectId; + // TODO: Initialize additional protected variables (engines, external services, + // etc.) - /** Values from {@code java/project.properties}. */ - protected ProjectProperties projectProperties; - - /** Stores the reactor result. */ protected NounMetadata result = null; - /** - * Runs {@link #preExecute()} for setup, then {@link #doExecute()} for business logic. If anything - * throws, the error is logged and returned as an error response instead of crashing the reactor. - * - * @return the result of reactor execution, or an error response if an exception occurs - */ + // Runs preExecute() for setup, then doExecute() for business logic. + // If anything throws, the error is logged and returned as an error response + // instead of crashing the reactor. @Override public NounMetadata execute() { try { @@ -65,10 +57,9 @@ public NounMetadata execute() { } } - /** - * Sets up project context before your reactor logic runs. Override this to add your own - * initialization (e.g. loading engines), but always call {@code super.preExecute()} first. - */ + // Sets up project context before your reactor logic runs. + // Override this to add your own initialization (e.g. loading engines), + // but always call super.preExecute() first. protected void preExecute() { // Resolve the project ID from the insight context projectId = this.insight.getContextProjectId(); @@ -86,13 +77,9 @@ protected void preExecute() { organizeKeys(); // Populates this.keyValue from the Pixel command arguments } - /** - * Extracts a {@link Map} parameter from the Pixel command. Useful when the frontend passes JSON - * objects as parameters. - * - * @param paramName the name of the map parameter to extract - * @return the map value, or {@code null} if no map parameter is found - */ + // Helper to extract a Map parameter from the Pixel command. + // Useful when the frontend passes JSON objects as parameters. + // Returns null if no map parameter is found. @SuppressWarnings("unchecked") protected Map getMap(String paramName) { GenRowStruct mapGrs = this.store.getGenRowStruct(paramName); @@ -111,11 +98,8 @@ protected Map getMap(String paramName) { return null; } - /** - * Implement this in your reactor subclass. This is where your business logic goes. Access - * parameters via {@code this.keyValue}. - * - * @return the result wrapped in {@link NounMetadata} - */ + // Implement this in your reactor subclass. + // This is where your business logic goes. Access parameters via this.keyValue. + // Return your result wrapped in NounMetadata. protected abstract NounMetadata doExecute(); } diff --git a/java/src/reactors/GetWeatherReactor.java b/java/src/reactors/GetWeatherReactor.java index dc5d7dd..61fa20a 100644 --- a/java/src/reactors/GetWeatherReactor.java +++ b/java/src/reactors/GetWeatherReactor.java @@ -3,14 +3,10 @@ import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; -/** - * GetWeather reactor: returns a hardcoded forecast for a given city. - * - *

Called from the frontend as: {@code GetWeather(city=["Boston"])} - * - *

Note: SEMOSS strips the "Reactor" suffix, so {@code GetWeatherReactor} becomes {@code - * GetWeather()}. - */ +// GetWeather reactor: returns a hardcoded forecast for a given city. +// +// Called from the frontend as: GetWeather(city=["Boston"]) +// Note: SEMOSS strips the "Reactor" suffix, so GetWeatherReactor becomes GetWeather(). public class GetWeatherReactor extends AbstractProjectReactor { private static final String CITY_KEY = "city"; diff --git a/java/src/util/Constants.java b/java/src/util/Constants.java index 2b0adbb..0a0c676 100644 --- a/java/src/util/Constants.java +++ b/java/src/util/Constants.java @@ -1,13 +1,11 @@ package util; -/** - * Shared constants used across reactors and utilities. Add {@code static final} fields here for - * values referenced in multiple places. - * - *

Example: - * - *

public static final String DEFAULT_MODEL = "gpt-4"; public static final int MAX_RETRIES = 3; - */ +// Shared constants used across reactors and utilities. +// Add static final fields here for values referenced in multiple places. +// +// Example: +// public static final String DEFAULT_MODEL = "gpt-4"; +// public static final int MAX_RETRIES = 3; public class Constants { // TODO: Add your project constants here diff --git a/java/src/util/HelperMethods.java b/java/src/util/HelperMethods.java index 7534542..6d70b1b 100644 --- a/java/src/util/HelperMethods.java +++ b/java/src/util/HelperMethods.java @@ -1,12 +1,9 @@ package util; -/** - * Shared utility methods used across reactors. Add static helper methods here for logic reused in - * multiple reactors. - * - *

Example: - * - *

public static String sanitizeInput(String input) { ... } public static Map - * buildSuccessResponse(Object data) { ... } - */ +// Shared utility methods used across reactors. +// Add static helper methods here for logic reused in multiple reactors. +// +// Example: +// public static String sanitizeInput(String input) { ... } +// public static Map buildSuccessResponse(Object data) { ... } public class HelperMethods {} diff --git a/java/src/util/ProjectProperties.java b/java/src/util/ProjectProperties.java index 6086755..b554520 100644 --- a/java/src/util/ProjectProperties.java +++ b/java/src/util/ProjectProperties.java @@ -8,19 +8,15 @@ import prerna.util.AssetUtility; import prerna.util.Utility; -/** - * Loads configuration from {@code java/project.properties} and exposes values to reactors. - * - *

This is a singleton: {@link reactors.AbstractProjectReactor} calls {@link - * #getInstance(String)} during {@code preExecute()} to initialize it. After that, {@link - * #getInstance()} returns the cached instance. - * - *

To add a new property: - * - *

1. Add the {@code key=value} to {@code java/project.properties} 2. Add a private field and - * getter in this class 3. Read the value in {@link #loadProp(String)} using {@code - * projectProperties.getProperty("yourKey")} - */ +// Loads configuration from java/project.properties and exposes values to reactors. +// +// This is a singleton: AbstractProjectReactor calls getInstance(projectId) during +// preExecute() to initialize it. After that, getInstance() returns the cached instance. +// +// To add a new property: +// 1. Add the key=value to java/project.properties +// 2. Add a private field and getter in this class +// 3. Read the value in loadProp() using projectProperties.getProperty("yourKey") public class ProjectProperties { private static final Logger LOGGER = LogManager.getLogger(ProjectProperties.class); @@ -32,12 +28,7 @@ public class ProjectProperties { private ProjectProperties() {} - /** - * Returns the cached singleton. - * - * @return the singleton instance - * @throws RuntimeException if {@link #getInstance(String)} hasn't been called yet - */ + // Returns the cached singleton. Throws if getInstance(projectId) hasn't been called yet. public static ProjectProperties getInstance() { if (INSTANCE == null) { throw new RuntimeException("Unable to load project configuration"); @@ -45,12 +36,7 @@ public static ProjectProperties getInstance() { return INSTANCE; } - /** - * Returns the singleton, loading properties from disk on first call. - * - * @param projectId the SEMOSS project ID used to locate the properties file - * @return the singleton instance - */ + // First call: loads properties from disk. Subsequent calls: returns cached instance. public static ProjectProperties getInstance(String projectId) { if (INSTANCE == null) { loadProp(projectId); @@ -58,12 +44,8 @@ public static ProjectProperties getInstance(String projectId) { return INSTANCE; } - /** - * Reads {@code java/project.properties} and populates this instance's fields. If the file is - * missing or unreadable, {@code INSTANCE} stays {@code null} and a warning is logged. - * - * @param projectId the SEMOSS project ID used to locate the properties file - */ + // Reads java/project.properties and populates this instance's fields. + // If the file is missing or unreadable, INSTANCE stays null and a warning is logged. private static void loadProp(String projectId) { ProjectProperties newInstance = new ProjectProperties(); diff --git a/test/reactors/BaseReactorTest.java b/test/reactors/BaseReactorTest.java index a0c3211..b424d0e 100644 --- a/test/reactors/BaseReactorTest.java +++ b/test/reactors/BaseReactorTest.java @@ -25,60 +25,53 @@ import prerna.sablecc2.om.NounStore; import prerna.util.AssetUtility; -/** - * Base test class providing common mocking utilities and setup for reactor - * tests. - * All reactor test classes should extend this class to leverage shared test - * infrastructure. - * - * This class handles: - * - Mock setup and teardown for common SEMOSS components - * - Temporary directory management for project assets - * - User authentication mocking - * - Project property file creation - * - PyTranslator mocking for Python-based reactors - */ +// Base test class providing common mocking utilities and setup for reactor +// tests. +// All reactor test classes should extend this class to leverage shared test +// infrastructure. +// +// This class handles: +// - Mock setup and teardown for common SEMOSS components +// - Temporary directory management for project assets +// - User authentication mocking +// - Project property file creation +// - PyTranslator mocking for Python-based reactors public abstract class BaseReactorTest { - /** Mock insight providing context for reactor execution */ + // Mock insight providing context for reactor execution @Mock protected Insight insight; - /** Mock user for authentication context */ + // Mock user for authentication context @Mock protected User user; - /** Mock noun store for reactor parameter management */ + // Mock noun store for reactor parameter management @Mock protected NounStore nounStore; - /** Mock PyTranslator for Python integration testing */ + // Mock PyTranslator for Python integration testing @Mock protected PyTranslator pyTranslator; - /** Static mock for AssetUtility to control project asset paths */ + // Static mock for AssetUtility to control project asset paths protected MockedStatic assetUtilsMock; - /** AutoCloseable for managing Mockito annotations lifecycle */ + // AutoCloseable for managing Mockito annotations lifecycle private AutoCloseable mocks; - /** Temporary directory for test execution */ + // Temporary directory for test execution protected Path tempDir; - /** Fake project ID for testing */ + // Fake project ID for testing protected static final String TEST_PROJECT_ID = "test-project-id"; - /** Default test user name */ + // Default test user name protected static final String TEST_USER_NAME = "TestUser"; - /** - * Sets up the test environment before each test execution. - * This method initializes mocks, creates temporary directories, and sets up - * common mock behaviors for insight, user, and asset utilities. - * - * @param p Temporary directory provided by JUnit - * @throws IOException if file operations fail during setup - */ + // Sets up the test environment before each test execution. + // Initializes mocks, creates temporary directories, and sets up + // common mock behaviors for insight, user, and asset utilities. @BeforeEach void baseSetup(@TempDir Path p) throws IOException { tempDir = p; @@ -90,10 +83,8 @@ void baseSetup(@TempDir Path p) throws IOException { setupProjectProperties(); } - /** - * Sets up mock behaviors for the Insight object. - * Configures project ID retrieval and user context. - */ + // Sets up mock behaviors for the Insight object. + // Configures project ID retrieval and user context. protected void setupInsightMocks() { when(insight.getUser()).thenReturn(user); when(insight.getContextProjectId()).thenReturn(TEST_PROJECT_ID); @@ -101,10 +92,8 @@ protected void setupInsightMocks() { when(insight.getPyTranslator()).thenReturn(pyTranslator); } - /** - * Sets up mock behaviors for the User object. - * Creates a mock access token with test user credentials. - */ + // Sets up mock behaviors for the User object. + // Creates a mock access token with test user credentials. protected void setupUserMocks() { AccessToken token = new AccessToken(); token.setName(TEST_USER_NAME); @@ -112,24 +101,18 @@ protected void setupUserMocks() { when(user.getPrimaryLoginToken()).thenReturn(token); } - /** - * Sets up static mocking for AssetUtility. - * Configures the asset utility to return the temporary directory as the project - * assets folder. - */ + // Sets up static mocking for AssetUtility. + // Configures the asset utility to return the temporary directory as the project + // assets folder. protected void setupAssetUtilityMocks() { assetUtilsMock = Mockito.mockStatic(AssetUtility.class); assetUtilsMock.when(() -> AssetUtility.getProjectAssetsFolder(TEST_PROJECT_ID)) .thenReturn(tempDir.toAbsolutePath().toString()); } - /** - * Creates a project.properties file in the temporary directory. - * Subclasses can override {@link #configureProjectProperties(Properties)} to - * add custom properties. - * - * @throws IOException if file creation or writing fails - */ + // Creates a project.properties file in the temporary directory. + // Subclasses can override configureProjectProperties(Properties) to + // add custom properties. protected void setupProjectProperties() throws IOException { Properties props = new Properties(); configureProjectProperties(props); @@ -144,26 +127,16 @@ protected void setupProjectProperties() throws IOException { } } - /** - * Hook method for subclasses to configure custom project properties. - * Override this method to add specific properties needed for your reactor - * tests. - * - * @param props Properties object to configure - */ + // Hook method for subclasses to configure custom project properties. + // Override this method to add specific properties needed for your reactor + // tests. protected void configureProjectProperties(Properties props) { // Default implementation - subclasses can override to add custom properties } - /** - * Sets up mock behaviors for PyTranslator. - * Configures default responses for Python module loading and function - * execution. - * - * @param moduleName The name of the Python module to mock - * @param functionName The name of the Python function to mock - * @param returnValue The value to return when the function is called - */ + // Sets up mock behaviors for PyTranslator. + // Configures default responses for Python module loading and function + // execution. protected void setupPyTranslatorMocks(String moduleName, String functionName, Object returnValue) { when(pyTranslator.loadPythonModuleFromFile( Mockito.eq(insight), @@ -179,14 +152,8 @@ protected void setupPyTranslatorMocks(String moduleName, String functionName, Ob .thenReturn(returnValue); } - /** - * Creates a Python source file in the temporary directory for testing. - * Useful for reactors that need to load Python files. - * - * @param fileName The name of the Python file to create - * @param content The content of the Python file - * @throws IOException if file creation fails - */ + // Creates a Python source file in the temporary directory for testing. + // Useful for reactors that need to load Python files. protected void createPythonFile(String fileName, String content) throws IOException { Path pyDir = tempDir.resolve("py"); Files.createDirectories(pyDir); @@ -194,14 +161,8 @@ protected void createPythonFile(String fileName, String content) throws IOExcept Files.writeString(pythonFile, content); } - /** - * Helper method to set a parameter value on a reactor. - * This directly sets the value in the reactor's keyValue map via reflection. - * - * @param reactor The reactor to set the parameter on - * @param key The parameter key - * @param value The parameter value - */ + // Helper method to set a parameter value on a reactor. + // This directly sets the value in the reactor's keyValue map via reflection. protected void setReactorParameter(reactors.AbstractProjectReactor reactor, String key, String value) { try { // Access the protected keyValue map field via reflection @@ -225,9 +186,7 @@ protected void setReactorParameter(reactors.AbstractProjectReactor reactor, Stri } } - /** - * Helper method to find a field in the class hierarchy. - */ + // Helper method to find a field in the class hierarchy. private java.lang.reflect.Field findField(Class clazz, String fieldName) throws NoSuchFieldException { Class current = clazz; while (current != null) { @@ -240,24 +199,14 @@ private java.lang.reflect.Field findField(Class clazz, String fieldName) thro throw new NoSuchFieldException(fieldName); } - /** - * Helper method to set a numeric parameter on a reactor. - * Converts the number to a string automatically. - * - * @param reactor The reactor to set the parameter on - * @param key The parameter key - * @param value The numeric value - */ + // Helper method to set a numeric parameter on a reactor. + // Converts the number to a string automatically. protected void setReactorParameter(reactors.AbstractProjectReactor reactor, String key, int value) { setReactorParameter(reactor, key, String.valueOf(value)); } - /** - * Cleans up test resources after each test execution. - * Closes static mocks and Mockito annotations. - * - * @throws Exception if cleanup fails - */ + // Cleans up test resources after each test execution. + // Closes static mocks and Mockito annotations. @AfterEach void baseTearDown() throws Exception { if (assetUtilsMock != null) { diff --git a/test/reactors/GetWeatherReactorTest.java b/test/reactors/GetWeatherReactorTest.java index 6b2abd9..1fe2dfa 100644 --- a/test/reactors/GetWeatherReactorTest.java +++ b/test/reactors/GetWeatherReactorTest.java @@ -12,18 +12,16 @@ import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.nounmeta.NounMetadata; -/** - * Tests for {@link GetWeatherReactor}. - * - * Verifies the reactor's output for valid cities, missing parameters, - * cities with spaces, and reactor/key description methods. - */ +// Tests for GetWeatherReactor. +// +// Verifies the reactor's output for valid cities, missing parameters, +// cities with spaces, and reactor/key description methods. @DisplayName("GetWeatherReactor Tests") public class GetWeatherReactorTest extends BaseReactorTest { private GetWeatherReactor reactor; - /** Initializes a fresh {@link GetWeatherReactor} before each test. */ + // Initializes a fresh GetWeatherReactor before each test. @BeforeEach void setup() { reactor = new GetWeatherReactor(); @@ -31,7 +29,7 @@ void setup() { reactor.setNounStore(nounStore); } - /** Verifies that a valid city returns the expected sunny forecast string. */ + // Verifies that a valid city returns the expected sunny forecast string. @Test @DisplayName("Should return sunny forecast for a given city") void testGetWeather_validCity() { @@ -45,7 +43,7 @@ void testGetWeather_validCity() { assertEquals("It will be sunny in Boston today.", forecast); } - /** Verifies that the city name is included in the response for any city. */ + // Verifies that the city name is included in the response for any city. @Test @DisplayName("Should include city name in response for different cities") void testGetWeather_differentCity() { @@ -58,10 +56,8 @@ void testGetWeather_differentCity() { assertTrue(forecast.contains("Tokyo")); } - /** - * Verifies that omitting the city parameter results in an error response. - * {@link AbstractProjectReactor} wraps unhandled exceptions as error responses. - */ + // Verifies that omitting the city parameter results in an error response. + // AbstractProjectReactor wraps unhandled exceptions as error responses. @Test @DisplayName("Should return error when city parameter is missing") void testGetWeather_missingCity() { @@ -76,7 +72,7 @@ void testGetWeather_missingCity() { ); } - /** Verifies that city names containing spaces are handled correctly. */ + // Verifies that city names containing spaces are handled correctly. @Test @DisplayName("Should handle city with spaces") void testGetWeather_cityWithSpaces() { @@ -89,7 +85,7 @@ void testGetWeather_cityWithSpaces() { assertEquals("It will be sunny in New York today.", forecast); } - /** Verifies that {@code getReactorDescription()} returns a non-null weather-related string. */ + // Verifies that getReactorDescription() returns a non-null weather-related string. @Test @DisplayName("Should return correct reactor description") void testGetReactorDescription() { @@ -98,7 +94,7 @@ void testGetReactorDescription() { assertTrue(desc.toLowerCase().contains("weather")); } - /** Verifies that {@code getDescriptionForKey("city")} returns a city-related description. */ + // Verifies that getDescriptionForKey("city") returns a city-related description. @Test @DisplayName("Should return correct description for city key") void testGetDescriptionForKey_city() { @@ -107,7 +103,7 @@ void testGetDescriptionForKey_city() { assertTrue(desc.toLowerCase().contains("city")); } - /** Verifies that {@code getDescriptionForKey()} returns {@code null} for unknown keys. */ + // Verifies that getDescriptionForKey() returns null for unknown keys. @Test @DisplayName("Should return null description for unknown key") void testGetDescriptionForKey_unknown() { diff --git a/test/reactors/ReactorTestSuite.java b/test/reactors/ReactorTestSuite.java index 0096c29..a881a9a 100644 --- a/test/reactors/ReactorTestSuite.java +++ b/test/reactors/ReactorTestSuite.java @@ -4,11 +4,9 @@ import org.junit.platform.suite.api.Suite; import org.junit.platform.suite.api.SuiteDisplayName; -/** - * Test suite that runs all reactor tests in the project. - * - * mvn test -Dtest=ReactorTestSuite - */ +// Test suite that runs all reactor tests in the project. +// +// mvn test -Dtest=ReactorTestSuite @Suite @SuiteDisplayName("Reactor Test Suite") @SelectClasses({ From 6c2f00088e84c8646986cd451e7740247d5c7676 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Mon, 13 Apr 2026 15:29:39 -0400 Subject: [PATCH 16/31] docs: comments in pom --- pom.xml | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index efe3019..75e1313 100644 --- a/pom.xml +++ b/pom.xml @@ -12,14 +12,20 @@ SemossTemplate SemossTemplate ${ci.version} + 0.0.1-SNAPSHOT + java/src + test + classes + test-classes + java @@ -28,11 +34,13 @@ + test/resources + maven-compiler-plugin @@ -44,7 +52,9 @@ + + central Maven Repository Switchboard @@ -54,6 +64,7 @@ false + 3rdPartyJARs Maven repository @@ -65,6 +76,7 @@ true + public Sonatype's Maven repository @@ -76,6 +88,7 @@ false + sonatype-snapshots Sonatype Snapshots @@ -87,28 +100,33 @@ + org.semoss semoss ${ci.version} provided - - + + org.junit.jupiter junit-jupiter 6.0.0 test - + org.junit.platform junit-platform-suite 6.0.0 test - + org.mockito mockito-core From a75bab7d26ef8518e4caa434777c59d1ed9941db Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Mon, 13 Apr 2026 15:34:45 -0400 Subject: [PATCH 17/31] fix: remove unneccessary repo --- pom.xml | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/pom.xml b/pom.xml index 75e1313..faf21db 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ - + @@ -76,28 +76,6 @@ true - - - public - Sonatype's Maven repository - https://oss.sonatype.org/content/groups/public - - true - - - false - - - - - sonatype-snapshots - Sonatype Snapshots - https://central.sonatype.com/repository/maven-snapshots - - true - always - - From 4e6b140278719caecd7d04af4e3c7b5ba63d7d16 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Tue, 14 Apr 2026 15:51:16 -0400 Subject: [PATCH 18/31] fix: bump snapshot --- .github/workflows/unit-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index a6f454c..f1f7a3b 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -7,10 +7,10 @@ on: snapshot: description: 'CI version snapshot' required: true - default: '5.2.0-SNAPSHOT' + default: '5.3.0-SNAPSHOT' type: choice options: - - '5.2.0-SNAPSHOT' + - '5.3.0-SNAPSHOT' # Add other snapshot versions as needed here! clean_install: description: 'Run `mvn clean install -U` to force-update dependencies' @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest container: maven:3.9.9-amazoncorretto-21-debian env: - SNAPSHOT: ${{ inputs.snapshot || '5.2.0-SNAPSHOT' }} + SNAPSHOT: ${{ inputs.snapshot || '5.3.0-SNAPSHOT' }} CLEAN_INSTALL: ${{ inputs.clean_install || 'false' }} steps: From 6b7528b568a5e0119a907d576208092417943c14 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Tue, 14 Apr 2026 15:57:24 -0400 Subject: [PATCH 19/31] Revert "fix: remove unneccessary repo" This reverts commit a75bab7d26ef8518e4caa434777c59d1ed9941db. --- pom.xml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index faf21db..75e1313 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ - + @@ -76,6 +76,28 @@ true + + + public + Sonatype's Maven repository + https://oss.sonatype.org/content/groups/public + + true + + + false + + + + + sonatype-snapshots + Sonatype Snapshots + https://central.sonatype.com/repository/maven-snapshots + + true + always + + From 3708da76c7b7947203fcf0087f6edd0b5ead7bf4 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 11:23:45 -0400 Subject: [PATCH 20/31] ci: use codebuild and switch container to use ecr public mirror --- .github/workflows/pre-commit.yml | 2 +- .github/workflows/unit-test.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 43d5e2d..cfe6109 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -6,7 +6,7 @@ on: jobs: pre-commit: - runs-on: ubuntu-latest + runs-on: codebuild-semoss-github-runner-${{ github.run_id }}-${{ github.run_attempt }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index f1f7a3b..86d8535 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -21,8 +21,8 @@ on: jobs: test: name: Run Test Suite - runs-on: ubuntu-latest - container: maven:3.9.9-amazoncorretto-21-debian + runs-on: codebuild-semoss-github-runner-${{ github.run_id }}-${{ github.run_attempt }} + container: public.ecr.aws/docker/library/maven:3.9.9-amazoncorretto-21-debian env: SNAPSHOT: ${{ inputs.snapshot || '5.3.0-SNAPSHOT' }} CLEAN_INSTALL: ${{ inputs.clean_install || 'false' }} From d1e275bad91ce37667b9e9f9e0f6609db36882bf Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 11:36:24 -0400 Subject: [PATCH 21/31] fix: try fix --- .github/workflows/pre-commit.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index cfe6109..9a66f72 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -7,6 +7,7 @@ on: jobs: pre-commit: runs-on: codebuild-semoss-github-runner-${{ github.run_id }}-${{ github.run_attempt }} + container: public.ecr.aws/docker/library/python:3.12-bookworm steps: - uses: actions/checkout@v4 @@ -29,11 +30,7 @@ jobs: echo "No client directory found, skipping frontend linting" fi - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.12.9' - + # Python 3.12 is provided by the container # Official pre-commit action handles caching automatically - uses: pre-commit/action@v3.0.1 with: From 4b95bbbcfc148481bc7132caf1432744bc6c0609 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 11:41:34 -0400 Subject: [PATCH 22/31] fix: pre-commit setup --- .github/workflows/pre-commit.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 9a66f72..df53c05 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -31,7 +31,17 @@ jobs: fi # Python 3.12 is provided by the container - # Official pre-commit action handles caching automatically - - uses: pre-commit/action@v3.0.1 + - name: Cache pre-commit environments + uses: actions/cache@v4 with: - extra_args: --all-files --show-diff-on-failure + path: ~/.cache/pre-commit + key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} + restore-keys: | + pre-commit-${{ runner.os }}- + + - name: Run pre-commit + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + export PATH="$HOME/.local/bin:$PATH" + uv tool install pre-commit + uv tool run pre-commit run --all-files --show-diff-on-failure From e2555115dbdd0fa3885329bbe3910a1947cc8815 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 11:43:55 -0400 Subject: [PATCH 23/31] fix: pre-commit setup --- .github/workflows/pre-commit.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index df53c05..bb54d34 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -31,13 +31,17 @@ jobs: fi # Python 3.12 is provided by the container + - name: Compute pre-commit cache key + id: pc-key + run: echo "hash=$(sha256sum .pre-commit-config.yaml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + - name: Cache pre-commit environments uses: actions/cache@v4 with: path: ~/.cache/pre-commit - key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} + key: pre-commit-${{ steps.pc-key.outputs.hash }} restore-keys: | - pre-commit-${{ runner.os }}- + pre-commit- - name: Run pre-commit run: | From 6b1aa0100dfdbf1a5bdcf1b088d89f5a9e94d237 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 11:50:42 -0400 Subject: [PATCH 24/31] fix: pre-commit setup --- .github/workflows/pre-commit.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index bb54d34..fe3e592 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -43,6 +43,12 @@ jobs: restore-keys: | pre-commit- + - name: Install OpenJDK 21 headless (required by pretty-format-java hook) + run: | + apt-get update + apt-get install -y --no-install-recommends openjdk-21-jre-headless + java -version + - name: Run pre-commit run: | curl -LsSf https://astral.sh/uv/install.sh | sh From af74149ccb271cefd09c6dedb66f1e0cb564c36f Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 11:56:50 -0400 Subject: [PATCH 25/31] fix: pre-commit setup --- .github/workflows/pre-commit.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index fe3e592..a8afa1a 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -43,10 +43,10 @@ jobs: restore-keys: | pre-commit- - - name: Install OpenJDK 21 headless (required by pretty-format-java hook) + - name: Install OpenJDK 17 headless (required by pretty-format-java hook) run: | apt-get update - apt-get install -y --no-install-recommends openjdk-21-jre-headless + apt-get install -y --no-install-recommends openjdk-17-jre-headless java -version - name: Run pre-commit From 2224c180de7129470417b3cb5b44bc05a55429ae Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 12:10:23 -0400 Subject: [PATCH 26/31] fix: theoretically fixed pre-commit, start to fix unit-test --- .github/workflows/pre-commit.yml | 48 ++++++++++++++++++++++---------- .github/workflows/unit-test.yml | 20 +++++++++++-- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index a8afa1a..0c16d40 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,16 +11,18 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '24' - - name: Install pnpm uses: pnpm/action-setup@v4 with: version: 10 - + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + cache: 'pnpm' + cache-dependency-path: 'client/pnpm-lock.yaml' + - name: Install dependencies and run frontend linting run: | if [ -d "client" ]; then @@ -29,19 +31,30 @@ jobs: else echo "No client directory found, skipping frontend linting" fi - + # Python 3.12 is provided by the container - - name: Compute pre-commit cache key - id: pc-key - run: echo "hash=$(sha256sum .pre-commit-config.yaml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + - name: Compute cache keys + id: keys + run: | + echo "pre-commit=$(sha256sum .pre-commit-config.yaml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - - name: Cache pre-commit environments + - name: Cache pre-commit hook environments uses: actions/cache@v4 with: path: ~/.cache/pre-commit - key: pre-commit-${{ steps.pc-key.outputs.hash }} + key: pre-commit-py3.12-${{ steps.keys.outputs.pre-commit }} restore-keys: | - pre-commit- + pre-commit-py3.12- + + - name: Cache uv downloads + uses: actions/cache@v4 + with: + path: | + ~/.cache/uv + ~/.local/share/uv + key: uv-py3.12-${{ runner.os }} + restore-keys: | + uv-py3.12- - name: Install OpenJDK 17 headless (required by pretty-format-java hook) run: | @@ -49,9 +62,14 @@ jobs: apt-get install -y --no-install-recommends openjdk-17-jre-headless java -version + - name: Install uv + run: | + if ! command -v uv >/dev/null 2>&1; then + curl -LsSf https://astral.sh/uv/install.sh | sh + fi + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + - name: Run pre-commit run: | - curl -LsSf https://astral.sh/uv/install.sh | sh - export PATH="$HOME/.local/bin:$PATH" uv tool install pre-commit uv tool run pre-commit run --all-files --show-diff-on-failure diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 86d8535..62646a4 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -30,15 +30,29 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + + # Computes a cache key from the root pom.xml. This project is a single-module + # Maven project, so hashing the root pom.xml alone is sufficient. + # + # For a multi-module Maven project, hash every pom.xml in the tree instead: + # run: echo "hash=$(find . -name pom.xml -not -path '*/target/*' -print0 \ + # | sort -z | xargs -0 sha256sum | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + # + # Note: we compute the hash in a shell step instead of using the built-in + # hashFiles() expression because hashFiles() fails on this self-hosted + # CodeBuild runner due to a workspace-path resolution issue. + - name: Compute pom.xml hash + id: pom-hash + run: echo "hash=$(sha256sum pom.xml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + - name: Cache Maven packages id: cache-maven uses: actions/cache@v4 with: path: /root/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.SNAPSHOT }} + key: ${{ runner.os }}-maven-${{ steps.pom-hash.outputs.hash }}-${{ env.SNAPSHOT }} restore-keys: | - ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}- + ${{ runner.os }}-maven-${{ steps.pom-hash.outputs.hash }}- ${{ runner.os }}-maven- - name: Maven Install From 7026176720da18c937a5de14308748e40855e409 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 12:40:28 -0400 Subject: [PATCH 27/31] docs: docs --- .github/workflows/pre-commit.yml | 65 +++++--------------- .github/workflows/pre-commit.yml.disabled | 54 +++++++++++++++++ .github/workflows/unit-test.yml | 24 ++------ .github/workflows/unit-test.yml.disabled | 73 +++++++++++++++++++++++ 4 files changed, 147 insertions(+), 69 deletions(-) create mode 100644 .github/workflows/pre-commit.yml.disabled create mode 100644 .github/workflows/unit-test.yml.disabled diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 0c16d40..43d5e2d 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -6,23 +6,20 @@ on: jobs: pre-commit: - runs-on: codebuild-semoss-github-runner-${{ github.run_id }}-${{ github.run_attempt }} - container: public.ecr.aws/docker/library/python:3.12-bookworm + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 10 - - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '24' - cache: 'pnpm' - cache-dependency-path: 'client/pnpm-lock.yaml' - + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Install dependencies and run frontend linting run: | if [ -d "client" ]; then @@ -31,45 +28,13 @@ jobs: else echo "No client directory found, skipping frontend linting" fi - - # Python 3.12 is provided by the container - - name: Compute cache keys - id: keys - run: | - echo "pre-commit=$(sha256sum .pre-commit-config.yaml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - - - name: Cache pre-commit hook environments - uses: actions/cache@v4 + + - name: Set up Python + uses: actions/setup-python@v4 with: - path: ~/.cache/pre-commit - key: pre-commit-py3.12-${{ steps.keys.outputs.pre-commit }} - restore-keys: | - pre-commit-py3.12- - - - name: Cache uv downloads - uses: actions/cache@v4 + python-version: '3.12.9' + + # Official pre-commit action handles caching automatically + - uses: pre-commit/action@v3.0.1 with: - path: | - ~/.cache/uv - ~/.local/share/uv - key: uv-py3.12-${{ runner.os }} - restore-keys: | - uv-py3.12- - - - name: Install OpenJDK 17 headless (required by pretty-format-java hook) - run: | - apt-get update - apt-get install -y --no-install-recommends openjdk-17-jre-headless - java -version - - - name: Install uv - run: | - if ! command -v uv >/dev/null 2>&1; then - curl -LsSf https://astral.sh/uv/install.sh | sh - fi - echo "$HOME/.local/bin" >> "$GITHUB_PATH" - - - name: Run pre-commit - run: | - uv tool install pre-commit - uv tool run pre-commit run --all-files --show-diff-on-failure + extra_args: --all-files --show-diff-on-failure diff --git a/.github/workflows/pre-commit.yml.disabled b/.github/workflows/pre-commit.yml.disabled new file mode 100644 index 0000000..cd21b6e --- /dev/null +++ b/.github/workflows/pre-commit.yml.disabled @@ -0,0 +1,54 @@ +# ----------------------------------------------------------------------------- +# DISABLED WORKFLOW (kept for reference) +# +# This is the default pre-commit workflow intended to run on GitHub-hosted +# `ubuntu-latest` runners. It has been disabled by renaming the file to end in +# `.yml.disabled` so GitHub Actions will not discover or execute it. The active +# workflow used by this repo is `pre-commit.yml`, which targets the SEMOSS +# self-hosted CodeBuild runner (`codebuild-semoss-github-runner-...`). +# +# To restore this default workflow (e.g., if migrating back to GitHub-hosted +# runners), rename this file back to `pre-commit-default.yml` and remove or +# rename the self-hosted variant. +# ----------------------------------------------------------------------------- + +name: Pre-commit + +on: + pull_request: + workflow_dispatch: + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Install dependencies and run frontend linting + run: | + if [ -d "client" ]; then + pnpm install + pnpm exec biome format --write && pnpm exec biome lint + else + echo "No client directory found, skipping frontend linting" + fi + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12.9' + + # Official pre-commit action handles caching automatically + - uses: pre-commit/action@v3.0.1 + with: + extra_args: --all-files --show-diff-on-failure diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 62646a4..f1f7a3b 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -21,8 +21,8 @@ on: jobs: test: name: Run Test Suite - runs-on: codebuild-semoss-github-runner-${{ github.run_id }}-${{ github.run_attempt }} - container: public.ecr.aws/docker/library/maven:3.9.9-amazoncorretto-21-debian + runs-on: ubuntu-latest + container: maven:3.9.9-amazoncorretto-21-debian env: SNAPSHOT: ${{ inputs.snapshot || '5.3.0-SNAPSHOT' }} CLEAN_INSTALL: ${{ inputs.clean_install || 'false' }} @@ -30,29 +30,15 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - - # Computes a cache key from the root pom.xml. This project is a single-module - # Maven project, so hashing the root pom.xml alone is sufficient. - # - # For a multi-module Maven project, hash every pom.xml in the tree instead: - # run: echo "hash=$(find . -name pom.xml -not -path '*/target/*' -print0 \ - # | sort -z | xargs -0 sha256sum | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - # - # Note: we compute the hash in a shell step instead of using the built-in - # hashFiles() expression because hashFiles() fails on this self-hosted - # CodeBuild runner due to a workspace-path resolution issue. - - name: Compute pom.xml hash - id: pom-hash - run: echo "hash=$(sha256sum pom.xml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - + - name: Cache Maven packages id: cache-maven uses: actions/cache@v4 with: path: /root/.m2/repository - key: ${{ runner.os }}-maven-${{ steps.pom-hash.outputs.hash }}-${{ env.SNAPSHOT }} + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.SNAPSHOT }} restore-keys: | - ${{ runner.os }}-maven-${{ steps.pom-hash.outputs.hash }}- + ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}- ${{ runner.os }}-maven- - name: Maven Install diff --git a/.github/workflows/unit-test.yml.disabled b/.github/workflows/unit-test.yml.disabled new file mode 100644 index 0000000..b54c8c1 --- /dev/null +++ b/.github/workflows/unit-test.yml.disabled @@ -0,0 +1,73 @@ +# ----------------------------------------------------------------------------- +# DISABLED WORKFLOW (kept for reference) +# +# This is the default unit-test workflow intended to run on GitHub-hosted +# `ubuntu-latest` runners. It has been disabled by renaming the file to end in +# `.yml.disabled` so GitHub Actions will not discover or execute it. The active +# workflow used by this repo is `unit-test.yml`, which targets the SEMOSS +# self-hosted CodeBuild runner (`codebuild-semoss-github-runner-...`). +# +# To restore this default workflow (e.g., if migrating back to GitHub-hosted +# runners), rename this file back to `unit-test-default.yml` and remove or +# rename the self-hosted variant. +# ----------------------------------------------------------------------------- + +name: Run Unit Tests + +on: + pull_request: + workflow_dispatch: + inputs: + snapshot: + description: 'CI version snapshot' + required: true + default: '5.3.0-SNAPSHOT' + type: choice + options: + - '5.3.0-SNAPSHOT' + # Add other snapshot versions as needed here! + clean_install: + description: 'Run `mvn clean install -U` to force-update dependencies' + required: true + default: false + type: boolean + +jobs: + test: + name: Run Test Suite + runs-on: ubuntu-latest + container: maven:3.9.9-amazoncorretto-21-debian + env: + SNAPSHOT: ${{ inputs.snapshot || '5.3.0-SNAPSHOT' }} + CLEAN_INSTALL: ${{ inputs.clean_install || 'false' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Cache Maven packages + id: cache-maven + uses: actions/cache@v4 + with: + path: /root/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.SNAPSHOT }} + restore-keys: | + ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}- + ${{ runner.os }}-maven- + + - name: Maven Install + if: steps.cache-maven.outputs.cache-hit != 'true' || env.CLEAN_INSTALL == 'true' + run: | + echo "Clean install: ${CLEAN_INSTALL}" + echo "Version snapshot: ${SNAPSHOT}" + if [ "${CLEAN_INSTALL}" = "true" ]; then + echo "Running mvn clean install with -U flag" + mvn -B clean install -U -DskipTests=true -Dci.version="${SNAPSHOT}" + else + echo "Running mvn install without -U flag" + mvn -B install -DskipTests=true -Dci.version="${SNAPSHOT}" + fi + + - name: Run Unit Tests + run: mvn -B ${{ steps.cache-maven.outputs.cache-hit == 'true' && '-o' || '' }} test -Dci.version="${SNAPSHOT}" + \ No newline at end of file From d8428450cf347ae90f5525c6c453d077c5e2c2d8 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 14:35:48 -0400 Subject: [PATCH 28/31] chore: move into folder --- .github/workflows/{ => disabled}/pre-commit.yml.disabled | 0 .github/workflows/{ => disabled}/unit-test.yml.disabled | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{ => disabled}/pre-commit.yml.disabled (100%) rename .github/workflows/{ => disabled}/unit-test.yml.disabled (100%) diff --git a/.github/workflows/pre-commit.yml.disabled b/.github/workflows/disabled/pre-commit.yml.disabled similarity index 100% rename from .github/workflows/pre-commit.yml.disabled rename to .github/workflows/disabled/pre-commit.yml.disabled diff --git a/.github/workflows/unit-test.yml.disabled b/.github/workflows/disabled/unit-test.yml.disabled similarity index 100% rename from .github/workflows/unit-test.yml.disabled rename to .github/workflows/disabled/unit-test.yml.disabled From 93b4d5a2f2f8bd2ded373edf0d963580a6ae0d79 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 14:39:13 -0400 Subject: [PATCH 29/31] Revert "docs: docs" This reverts commit 7026176720da18c937a5de14308748e40855e409 but maintains the disabled tests in the disabled folder --- .github/workflows/pre-commit.yml | 65 ++++++++++++++++++++++++-------- .github/workflows/unit-test.yml | 24 +++++++++--- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 43d5e2d..0c16d40 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -6,20 +6,23 @@ on: jobs: pre-commit: - runs-on: ubuntu-latest + runs-on: codebuild-semoss-github-runner-${{ github.run_id }}-${{ github.run_attempt }} + container: public.ecr.aws/docker/library/python:3.12-bookworm steps: - uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '24' - - name: Install pnpm uses: pnpm/action-setup@v4 with: version: 10 - + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + cache: 'pnpm' + cache-dependency-path: 'client/pnpm-lock.yaml' + - name: Install dependencies and run frontend linting run: | if [ -d "client" ]; then @@ -28,13 +31,45 @@ jobs: else echo "No client directory found, skipping frontend linting" fi - - - name: Set up Python - uses: actions/setup-python@v4 + + # Python 3.12 is provided by the container + - name: Compute cache keys + id: keys + run: | + echo "pre-commit=$(sha256sum .pre-commit-config.yaml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + + - name: Cache pre-commit hook environments + uses: actions/cache@v4 with: - python-version: '3.12.9' - - # Official pre-commit action handles caching automatically - - uses: pre-commit/action@v3.0.1 + path: ~/.cache/pre-commit + key: pre-commit-py3.12-${{ steps.keys.outputs.pre-commit }} + restore-keys: | + pre-commit-py3.12- + + - name: Cache uv downloads + uses: actions/cache@v4 with: - extra_args: --all-files --show-diff-on-failure + path: | + ~/.cache/uv + ~/.local/share/uv + key: uv-py3.12-${{ runner.os }} + restore-keys: | + uv-py3.12- + + - name: Install OpenJDK 17 headless (required by pretty-format-java hook) + run: | + apt-get update + apt-get install -y --no-install-recommends openjdk-17-jre-headless + java -version + + - name: Install uv + run: | + if ! command -v uv >/dev/null 2>&1; then + curl -LsSf https://astral.sh/uv/install.sh | sh + fi + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Run pre-commit + run: | + uv tool install pre-commit + uv tool run pre-commit run --all-files --show-diff-on-failure diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index f1f7a3b..62646a4 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -21,8 +21,8 @@ on: jobs: test: name: Run Test Suite - runs-on: ubuntu-latest - container: maven:3.9.9-amazoncorretto-21-debian + runs-on: codebuild-semoss-github-runner-${{ github.run_id }}-${{ github.run_attempt }} + container: public.ecr.aws/docker/library/maven:3.9.9-amazoncorretto-21-debian env: SNAPSHOT: ${{ inputs.snapshot || '5.3.0-SNAPSHOT' }} CLEAN_INSTALL: ${{ inputs.clean_install || 'false' }} @@ -30,15 +30,29 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + + # Computes a cache key from the root pom.xml. This project is a single-module + # Maven project, so hashing the root pom.xml alone is sufficient. + # + # For a multi-module Maven project, hash every pom.xml in the tree instead: + # run: echo "hash=$(find . -name pom.xml -not -path '*/target/*' -print0 \ + # | sort -z | xargs -0 sha256sum | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + # + # Note: we compute the hash in a shell step instead of using the built-in + # hashFiles() expression because hashFiles() fails on this self-hosted + # CodeBuild runner due to a workspace-path resolution issue. + - name: Compute pom.xml hash + id: pom-hash + run: echo "hash=$(sha256sum pom.xml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + - name: Cache Maven packages id: cache-maven uses: actions/cache@v4 with: path: /root/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ env.SNAPSHOT }} + key: ${{ runner.os }}-maven-${{ steps.pom-hash.outputs.hash }}-${{ env.SNAPSHOT }} restore-keys: | - ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}- + ${{ runner.os }}-maven-${{ steps.pom-hash.outputs.hash }}- ${{ runner.os }}-maven- - name: Maven Install From 129bafee9c3f1c49169f0953ff8550f0c4fada56 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 14:47:46 -0400 Subject: [PATCH 30/31] chore: rename --- .github/workflows/{disabled => .disabled}/pre-commit.yml.disabled | 0 .github/workflows/{disabled => .disabled}/unit-test.yml.disabled | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{disabled => .disabled}/pre-commit.yml.disabled (100%) rename .github/workflows/{disabled => .disabled}/unit-test.yml.disabled (100%) diff --git a/.github/workflows/disabled/pre-commit.yml.disabled b/.github/workflows/.disabled/pre-commit.yml.disabled similarity index 100% rename from .github/workflows/disabled/pre-commit.yml.disabled rename to .github/workflows/.disabled/pre-commit.yml.disabled diff --git a/.github/workflows/disabled/unit-test.yml.disabled b/.github/workflows/.disabled/unit-test.yml.disabled similarity index 100% rename from .github/workflows/disabled/unit-test.yml.disabled rename to .github/workflows/.disabled/unit-test.yml.disabled From 486189944d5f02b6ca5ba488419ba0aee7ccc4b1 Mon Sep 17 00:00:00 2001 From: Rithvik Doshi Date: Fri, 17 Apr 2026 14:57:02 -0400 Subject: [PATCH 31/31] ci: try caching jdk --- .github/workflows/pre-commit.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 0c16d40..18f7d0e 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -56,10 +56,23 @@ jobs: restore-keys: | uv-py3.12- + - name: Cache OpenJDK 17 + id: cache-jdk + uses: actions/cache@v4 + with: + path: /usr/lib/jvm/java-17-openjdk-amd64 + key: jdk-17-bookworm-${{ runner.os }} + - name: Install OpenJDK 17 headless (required by pretty-format-java hook) + if: steps.cache-jdk.outputs.cache-hit != 'true' run: | apt-get update apt-get install -y --no-install-recommends openjdk-17-jre-headless + + - name: Set up Java environment + run: | + echo "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64" >> "$GITHUB_ENV" + echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> "$GITHUB_PATH" java -version - name: Install uv