diff --git a/Justfile b/Justfile
index 7205d769a..50c8cb6a6 100644
--- a/Justfile
+++ b/Justfile
@@ -2,19 +2,19 @@ set shell := ["bash", "-uc"]
# Migrate to local DB
migrate *args:
- dotenvx run -- ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db {{args}}
+ dotenvx run -- bash ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db {{args}}
# Drop local DB
drop *args:
- dotenvx run -- ./mvnw flyway:clean -Dflyway.locations=filesystem:./db -Dflyway.cleanDisabled=false {{args}}
+ dotenvx run -- bash ./mvnw flyway:clean -Dflyway.locations=filesystem:./db -Dflyway.cleanDisabled=false {{args}}
# Manually migrate STAGING DB if needed (CAREFUL!!!)
migrate-stg *args:
- dotenvx run -f .env.staging -- ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db/migration {{args}}
+ dotenvx run -f .env.staging -- bash ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db/migration {{args}}
# Manually migrate PROD DB if needed (CAREFUL!!!)
migrate-prod *args:
- dotenvx run -f .env.production -- ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db/migration {{args}}
+ dotenvx run -f .env.production -- bash ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db/migration {{args}}
# Manually copy production db to staging. (WILL DROP ALL STAGING DATA!!!) Check wiki for more details.
copy-stg *args:
@@ -22,7 +22,7 @@ copy-stg *args:
# Run the backend Spring server
backend-dev *args:
- dotenvx run -f .env -f .env.shared -- ./mvnw -Dspring-boot.run.profiles=dev spring-boot:run {{args}}
+ dotenvx run -f .env -f .env.shared -- bash ./mvnw -Dspring-boot.run.profiles=dev spring-boot:run {{args}}
# Run the leetcode auth stealer service
auth-dev *args:
@@ -34,30 +34,30 @@ auth-gen *args:
# Run the backend Spring server with an exposed debugger at :5005
backend-dev-debug *args:
- dotenvx run -- ./mvnw \
+ dotenvx run -- bash ./mvnw \
-Dspring-boot.run.profiles=dev \
-Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005" \
spring-boot:run {{args}}
# Builds and installs Spring backend
backend-install *args:
- ./mvnw install -DskipTests=true {{args}}
+ bash ./mvnw install -DskipTests=true {{args}}
# Run backend formatter (check only)
backend-spotless *args:
- ./mvnw spotless:check
+ bash ./mvnw spotless:check
# Run backend formatter (check & write)
backend-spotless-fix *args:
- ./mvnw spotless:apply
+ bash ./mvnw spotless:apply
# Run backend linter, formatter, & tests
backend-test *args:
- just backend-spotless && dotenvx run -f .env -f .env.shared -- ./mvnw checkstyle:check verify -Dspring.profiles.active=ci {{args}}
+ just backend-spotless && dotenvx run -f .env -f .env.shared -- bash ./mvnw checkstyle:check verify -Dspring.profiles.active=ci {{args}}
# Run backend tests with a debugger
backend-testd *args:
- just backend-spotless && dotenvx run -f .env -f .env.shared -- ./mvnw checkstyle:check verify -Dspring.profiles.active=ci -Dmaven.surefire.debug {{args}}
+ just backend-spotless && dotenvx run -f .env -f .env.shared -- bash ./mvnw checkstyle:check verify -Dspring.profiles.active=ci -Dmaven.surefire.debug {{args}}
# Run the frontend
frontend-dev *args:
diff --git a/README.md b/README.md
index 829614ca0..fd516474d 100644
--- a/README.md
+++ b/README.md
@@ -28,17 +28,17 @@ _Last updated: 02/14/2026_
Codebloom features include
-- [User profiles with submission history](./docs/features/USER-PROFILE.md)
-- [Automatic LeetCode syncing in the background](./docs/features/BGSYNC.md)
-- [University-specific leaderboards](./docs/features/LEADERBOARD.md)
-- [Problem Of The Day](./docs/features/POTD.md)
-- [Automatic achievements](./docs/features/ACHIEVEMENTS.md)
-- [Embeddable widgets](./docs/features/EMBED.md)
-- [Dynamic question scoring](./docs/features/SCORES.md)
-- [Live multiplayer duels](./docs/features/DUELS.md)
-- [Club/organization integrations via Discord servers](./docs/features/DISCORD-INTEGRATIONS.md)
-
-[Goto `docs/features` to view all feature documentation](./docs/features)
+- [User profiles with submission history](https://docs.patinanetwork.org/codebloom/features/USER-PROFILE/)
+- [Automatic LeetCode syncing in the background](https://docs.patinanetwork.org/codebloom/features/BGSYNC/)
+- [University-specific leaderboards](https://docs.patinanetwork.org/codebloom/features/LEADERBOARD/)
+- [Problem Of The Day](https://docs.patinanetwork.org/codebloom/features/POTD/)
+- [Automatic achievements](https://docs.patinanetwork.org/codebloom/features/ACHIEVEMENTS/)
+- [Embeddable widgets](https://docs.patinanetwork.org/codebloom/features/EMBED/)
+- [Dynamic question scoring](https://docs.patinanetwork.org/codebloom/features/SCORES/)
+- [Live multiplayer duels](https://docs.patinanetwork.org/codebloom/features/DUELS/)
+- [Club/organization integrations via Discord servers](https://docs.patinanetwork.org/codebloom/features/DISCORD-INTEGRATIONS/)
+
+[View all Codebloom documentation](https://docs.patinanetwork.org/codebloom/)
Codebloom is a public-first platform that does not require authentication to view most data, so please feel free to click around at [codebloom.patinanetwork.org](https://codebloom.patinanetwork.org)!
@@ -72,16 +72,16 @@ This repository is a monorepo that contains many different pieces of Codebloom,
- [Goto `.github/scripts/redeploy/` to view Bun Shell redeploy scripts](./.github/scripts/redeploy/)
- [Goto `infra/`](./infra/)
- [View detailed documentation](./infra/README.md)
- - [View observability documentation](./docs/observability/README.md)
+ - [View observability documentation](https://docs.patinanetwork.org/codebloom/observability/)
- Our internal tools (e.g. custom Ink CLI scripts & our internal standup bot) are currently being written in [Rust](https://rust-lang.org/) and [TypeScript](https://typescriptlang.org)
- [Goto `internal/`](./internal/)
- [View detailed documentation](./internal/README.md)
- Environment secrets live in local env files generated from Azure.
- - [View detailed documentation](./docs/local/SECRET.md)
+ - [View detailed documentation](https://docs.patinanetwork.org/codebloom/local/SECRET/)
## Setup
-Please go to [`docs/setup/README.md`](./docs/local/README.md) to begin setup instructions for local development.
+Please go to the [local development docs](https://docs.patinanetwork.org/codebloom/local/) to begin setup instructions for local development.
## Authors
diff --git a/docs/QUICK-LINKS.md b/docs/QUICK-LINKS.md
deleted file mode 100644
index 223fbda0d..000000000
--- a/docs/QUICK-LINKS.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# Quick Links
-
-- [Codebloom](https://codebloom.patinanetwork.org) ↗
-- [Codebloom - Swagger Documentation](https://codebloom.patinanetwork.org/swagger-ui/index.html) ↗
-- [Codebloom - Staging](https://stg.codebloom.patinanetwork.org) ↗
-- [Codebloom - Staging - Swagger Documentation](https://stg.codebloom.patinanetwork.org/swagger-ui/index.html) ↗
-- [Codebloom - Local - Swagger Documentation](http://localhost:8080/swagger-ui/index.html) ↗
-- [Codebloom - Local - Prometheus Metrics](http://localhost:8080/actuator/prometheus) ↗
-- [Grafana Dashboard - Separate domain](https://monitor.tahmid.io)
-- [Public Taskboard (Read-only)](https://codebloom.notion.site) ↗
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 000000000..2d7b4a1c9
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,4 @@
+# Documentation
+
+Documentation can be found at https://docs.patinanetwork.org/codebloom.
+
diff --git a/docs/ci/AUTO_APPROVALS.md b/docs/ci/AUTO_APPROVALS.md
deleted file mode 100644
index fdf72cd58..000000000
--- a/docs/ci/AUTO_APPROVALS.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Auto Approvals
-
-The auto-approval script ([.github/scripts/auto-approval/index.ts](.github/scripts/auto-approval/index.ts)) automatically approves pull requests based on two conditions.
-
-## How It Works
-
-When triggered, the script:
-
-1. Accepts `--githubToken`, `--repo`, and `--prId` as arguments.
-2. Loads `NOTION_TASK_DB_ID` and `NOTION_SECRET` from the env.
-3. Looks up the Notion task linked to the PR and reads its **Priority** property.
-4. Fetches the PR from GitHub to get the author's username.
-5. Checks whether the author is a repository collaborator.
-
-## Approval Conditions
-
-A PR is auto-approved if **either** condition is met:
-
-| Condition | Requirement |
-|---|---|
-| **Support ticket override** | PR's linked Notion task has Priority = `Support` **and** the PR author is a repository collaborator |
-| **Owner override** | PR author is an `AUTHORIZED_USER` |
-
-If neither condition is met, the script logs `"Skipping auto approval..."` and exits without approving.
-
-## Required Secrets / Environment
-
-| Variable | Source | Description |
-|---|---|---|
-| `GH_TOKEN` | CI secret | GitHub token used to fetch PR info and submit the review |
-| `NOTION_TASK_DB_ID` | `.env.ci` | Notion database ID for tasks |
-| `NOTION_SECRET` | `.env.ci` | Notion integration secret |
diff --git a/docs/features/ACHIEVEMENTS.md b/docs/features/ACHIEVEMENTS.md
deleted file mode 100644
index 907e7ed51..000000000
--- a/docs/features/ACHIEVEMENTS.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Achievements
-
-
-
- - - NOTE: The role mentions would point to actual users in production. - -
-- - NOTE: This screenshot is taken locally to avoid releasing private information from production. - -
-
- - - NOTE: The role mentions would point to actual users in production. - -
-- - NOTE: This screenshot is taken locally to avoid releasing private information from production. - -
-
-
-This slash command will return a refreshed leaderboard embed (similar to the automatic leaderboard embeds)
-
-Current restrcitions:
-
-- We cannot currently update the leaderboard via Discord (though we are working on that now!)
-- If the club requested a leaderboard update before 5 minutes have elapsed, the command will then fail & indicate how long to wait until you can request a new update.
- - **NOTE**: it will not show the fail response to every user, but only to the user who triggered the command.
-
-Here is an example of what it may look like:
-
-
diff --git a/docs/features/DUELS.md b/docs/features/DUELS.md
deleted file mode 100644
index 7d7c4b8a0..000000000
--- a/docs/features/DUELS.md
+++ /dev/null
@@ -1,5 +0,0 @@
-> [!CAUTION]
-> We apologize but this page is currently a work in progress
-> Reason: Currently still in development
-
-# Live Duels
diff --git a/docs/features/EMBED.md b/docs/features/EMBED.md
deleted file mode 100644
index ae5a0f9ec..000000000
--- a/docs/features/EMBED.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Embeddable widgets
-
-
-
-
-
-which will show you the same users, but their ranks will now indicate how they are doing across ALL users in Codebloom.
-
-The leaderboard pages are fully paginated, with custom filters & search queries.
-
-> [!NOTE]
-
-## `/leaderboard` - Current leaderboard
-
-This page will always show the currently active leaderboard.
-
-[Goto `codebloom.patinanetwork.org/leaderboard`](https://codebloom.patinanetwork.org/leaderboard)
-
-## `/leaderboard/all` - List of all leaderboards
-
-This page will show the list of all leaderboards. You can click on a previous leaderboard to view the leaderboard exactly as that leaderboard ended.
-
-[Goto `codebloom.patinanetwork.org/leaderboard/all`](https://codebloom.patinanetwork.org/leaderboard/all)
-
-## `/leaderboard/{leaderboardId}` - View specific ID
-
-This page will show the leaderboard which is connected to `{leaderboardId}`. This is in full feature parity with `/leaderboard`.
-
-[Goto `codebloom.patinanetwork.org/leaderboard/dcf49998-604b-4240-8711-4456c49d79e7`](https://codebloom.patinanetwork.org/leaderboard/dcf49998-604b-4240-8711-4456c49d79e7), the very first Codebloom leaderboard named `Beta Launch Leaderboard`
diff --git a/docs/features/POTD.md b/docs/features/POTD.md
deleted file mode 100644
index 98c26575d..000000000
--- a/docs/features/POTD.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# POTD - Problem Of The Day
-
-
-
-
-
-
-
-## Embedded POTD
-
-There is an embedded widget for the POTD, which can be attached to any website.
-
-[Goto `./EMBED.md` to learn more](./EMBED.md)
diff --git a/docs/features/SCORES.md b/docs/features/SCORES.md
deleted file mode 100644
index 95764d1a6..000000000
--- a/docs/features/SCORES.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Scores
-
-Codebloom has some custom algebraic functions to determine how points should be allocated depending on various properties.
-
-> [!NOTE]
-> We are always trying to improve our scoring formula, so if you have any suggestions - please open an [issue](https://github.com/tahminator/codebloom/issues/new) here.
-
-The actual file used to calculate scores is called [`ScoreCalculator.java`](../../src/main/java/org/patinanetwork/codebloom/common/leetcode/score/ScoreCalculator.java). This class essentially requires 3 properties to produce a score:
-
-- Difficulty (`EASY`, `MEDIUM`, `HARD`)
-- Acceptance Rate (`0.0` to `1.0`)
-- Multiplier (usually `1.0` unless it's a POTD problem which is then defined off mapped values based on problem difficulty + 5% deviation)
-
-You may notice that we call a `purpleFunction`, `blueFunction`, etc. These are custom algebraic functions we have defined based off of previous feedback & internal standards for what we think makes the most sense.
-
-These functions have a Desmos link to correlate the code to the graph inside of [`MultiplierFunctions.java`](../../src/main/java/org/patinanetwork/codebloom/common/leetcode/score/MultiplierFunctions.java), so feel free to view them to get the latest representation of any changes.
-
-_Last updated: 02/15/2026_
-
-Here is a current screenshot:
-
-
-Finally, we then apply a 5% deviation to the final result to have more variance in the outputted scores.
diff --git a/docs/features/USER-PROFILE.md b/docs/features/USER-PROFILE.md
deleted file mode 100644
index ab83beb0c..000000000
--- a/docs/features/USER-PROFILE.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# User profile
-
-
-
-
-
-
-
-Codebloom uses Spring Boot Actuator and Prometheus to provide operational insights into the running application. We also collect logs via OpenSearch, which is setup through the DigitalOcean App Platform [(view app spec here)](../../.do/specs.ts). Both of these data sources are then fed to a Grafana instance hosted on [monitor.tahmid.io](https://monitor.tahmid.io)
-
-## Grafana
-
-All metrics for production & staging can be viewed on [monitor.tahmid.io](https://monitor.tahmid.io).
-
-> [!NOTE]
-> Please reach out to [@tahminator](https://github.com/tahminator) if you are on the Codebloom dev team & need the credentials to login & view the dashboard
-
-## Actuator Endpoints
-
-Spring Boot Actuator exposes operational information about the running application through HTTP endpoints.
-
-### Security
-
-All actuator endpoints are protected with HTTP Basic Authentication to prevent unauthorized access to sensitive operational data.
-
-**Authentication Details:**
-
-- **Authentication Type:** HTTP Basic Auth
-- **Role Required:** `ACTUATOR`
-- **Credentials:** Stored in environment variables
- - `ACTUATOR_USERNAME` - Username for actuator endpoints
- - `ACTUATOR_PASSWORD` - Password for actuator endpoints (generate with `openssl rand -base64 48 | head -c 64`)
-
-### Available Endpoints
-
-Currently exposed endpoints:
-
-- **`/actuator/prometheus`** - Prometheus-formatted metrics endpoint for scraping
-
-## Prometheus Metrics
-
-Prometheus metrics provide detailed insights into application performance, JVM statistics, HTTP requests, and custom business metrics.
-
-### Accessing Metrics
-
-**Local Development:**
-
-```
-http://localhost:8080/actuator/prometheus
-```
-
-(Requires HTTP Basic Auth with actuator credentials)
-
-**Staging:**
-
-```
-https://stg.codebloom.patinanetwork.org/actuator/prometheus
-```
-
-**Production:**
-
-```
-https://codebloom.patinanetwork.org/actuator/prometheus
-```
-
-### Testing Locally
-
-To test the actuator endpoint locally:
-
-1. Ensure your `.env` file has `ACTUATOR_USERNAME` and `ACTUATOR_PASSWORD` set
-2. Start the application (`just dev`)
-3. Access the endpoint using curl:
-
-```bash
-curl -u actuator:your_password http://localhost:8080/actuator/prometheus
-```
-
-Or use a browser and enter the username/password when prompted.
diff --git a/docs/observability/dashboard.png b/docs/observability/dashboard.png
deleted file mode 100644
index eecb1c627..000000000
Binary files a/docs/observability/dashboard.png and /dev/null differ
diff --git a/e2e/README.md b/e2e/README.md
index 1e4ac3f7e..8d6da743b 100644
--- a/e2e/README.md
+++ b/e2e/README.md
@@ -9,7 +9,7 @@ These tests run daily at 12:18 ET every day under [.github/scripts/test/run-e2e-
## Requirements
-- `pnpm` - Follow the main developer installation instructions under [docs/local/SETUP.md](../docs/local/SETUP.md).
+- `pnpm` - Follow the main developer installation instructions under [local setup](https://docs.patinanetwork.org/codebloom/local/SETUP/).
## Run
diff --git a/email/README.md b/email/README.md
index f243cf4ac..377471e54 100644
--- a/email/README.md
+++ b/email/README.md
@@ -7,7 +7,7 @@ These templates are then built into `*.html` files which are then loaded at runt
### Requirements
- [pnpm](https://pnpm.io/)
- > It is **STRONGLY** recommended that you follow the setup directions for `pnpm` inside of [docs/setup/README.md](../../docs/setup/README.md)
+ > It is **STRONGLY** recommended that you follow the setup directions for `pnpm` inside of [local setup](https://docs.patinanetwork.org/codebloom/local/SETUP/)
### Run
diff --git a/src/README.md b/src/README.md
index 2573cf3e0..a035fccf7 100644
--- a/src/README.md
+++ b/src/README.md
@@ -14,7 +14,7 @@ View [STYLEGUIDE.md](../STYLEGUIDE.md) to view best practices.
## Requirements
> [!WARNING]
-> It is HIGHLY recommended that you follow the setup instructions at [docs/local/README.md](../docs/local/README.md) to get up and running.
+> It is HIGHLY recommended that you follow the setup instructions at [local development docs](https://docs.patinanetwork.org/codebloom/local/) to get up and running.
- [`openjdk@25`](https://openjdk.org/projects/jdk/25/) (though you may feel free to use any other distribution if you would like)
- [`mvn`](https://maven.apache.org/what-is-maven.html) - you do not need to explicitly download this, just use `./mvnw` from the root directory instead (our `Justfile` commands all use `./mvnw` as well).
@@ -22,7 +22,7 @@ View [STYLEGUIDE.md](../STYLEGUIDE.md) to view best practices.
## Run
> [!WARNING]
-> It is HIGHLY recommended that you follow the setup instructions at [docs/local/README.md](../docs/local/README.md) to get up and running.
+> It is HIGHLY recommended that you follow the setup instructions at [local development docs](https://docs.patinanetwork.org/codebloom/local/) to get up and running.
## Structure
diff --git a/src/main/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGenerator.java b/src/main/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGenerator.java
index 0680bf28a..bde3d687c 100644
--- a/src/main/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGenerator.java
+++ b/src/main/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGenerator.java
@@ -3,13 +3,17 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.annotations.VisibleForTesting;
+import java.io.File;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.patinanetwork.codebloom.shared.tag.ParentTags;
import org.patinanetwork.codebloom.shared.tag.TagMetadataList;
@@ -208,8 +212,11 @@ private void writeToFile() throws Exception {
Files.createDirectories(outputPath.getParent());
Files.writeString(outputPath, finalOutput.toString());
- Path prettierBin = Paths.get("js/node_modules/.bin/prettier").toAbsolutePath();
+ Path nodeExecutable = resolveNodeExecutable();
+ Path prettierBin =
+ Paths.get("js/node_modules/prettier/bin/prettier.cjs").toAbsolutePath();
new ProcessBuilder(
+ nodeExecutable.toString(),
prettierBin.toString(),
"--write",
outputPath.toAbsolutePath().toString())
@@ -219,4 +226,35 @@ private void writeToFile() throws Exception {
log.info("Generated TypeScript file: {} with {} import(s)", outputPath, imports.size());
}
+
+ private static Path resolveNodeExecutable() throws IOException {
+ boolean windows =
+ System.getProperty("os.name", "").toLowerCase(Locale.ROOT).contains("win");
+ String[] executableNames = windows ? new String[] {"node.exe", "node"} : new String[] {"node"};
+ return resolveExecutableOnPath(System.getenv("PATH"), executableNames);
+ }
+
+ @VisibleForTesting
+ static Path resolveExecutableOnPath(String pathEnv, String... executableNames) throws IOException {
+ if (pathEnv == null || pathEnv.isBlank()) {
+ throw new IOException("PATH is unset, so node cannot be located");
+ }
+ for (String directory : pathEnv.split(Pattern.quote(File.pathSeparator))) {
+ String trimmed = directory.trim();
+ if (trimmed.isEmpty()) {
+ continue;
+ }
+ Path directoryPath = Paths.get(trimmed);
+ if (!directoryPath.isAbsolute()) {
+ continue;
+ }
+ for (String executableName : executableNames) {
+ Path candidate = directoryPath.resolve(executableName);
+ if (Files.isRegularFile(candidate) && Files.isExecutable(candidate)) {
+ return candidate.toRealPath();
+ }
+ }
+ }
+ throw new IOException("node was not found in an absolute directory on PATH");
+ }
}
diff --git a/src/test/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGeneratorTest.java b/src/test/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGeneratorTest.java
index 4fa3bbe08..f20d7f935 100644
--- a/src/test/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGeneratorTest.java
+++ b/src/test/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGeneratorTest.java
@@ -1,10 +1,19 @@
package org.patinanetwork.codebloom.utilities.generator.complex;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
import static org.junit.jupiter.api.Assertions.*;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Set;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import org.patinanetwork.codebloom.common.db.models.question.topic.LeetcodeTopicEnum;
import org.patinanetwork.codebloom.common.db.models.usertag.Tag;
import org.patinanetwork.codebloom.shared.tag.TagMetadataList;
@@ -111,4 +120,29 @@ void topicMetadataListAllHaveNonEmptyName() {
assertFalse(metadata.getName().isBlank());
}
}
+
+ @Test
+ void resolveExecutableOnPathReturnsAbsoluteExecutable(@TempDir Path root) throws Exception {
+ Path blocked = root.resolve("blocked");
+ Path allowed = root.resolve("allowed");
+ Files.createDirectories(blocked);
+ Files.createDirectories(allowed);
+ Path blockedNode = blocked.resolve("node");
+ Path allowedNode = allowed.resolve("node");
+ Files.writeString(blockedNode, "");
+ Files.writeString(allowedNode, "");
+ Files.setPosixFilePermissions(blockedNode, Set.of(OWNER_READ, OWNER_WRITE));
+ Files.setPosixFilePermissions(allowedNode, Set.of(OWNER_READ, OWNER_EXECUTE));
+
+ String pathEnv = blocked + File.pathSeparator + "relative-bin" + File.pathSeparator + allowed;
+
+ assertEquals(allowedNode.toRealPath(), ComplexJSTypesGenerator.resolveExecutableOnPath(pathEnv, "node"));
+ }
+
+ @Test
+ void resolveExecutableOnPathRejectsRelativeOrMissingEntries() {
+ assertThrows(IOException.class, () -> ComplexJSTypesGenerator.resolveExecutableOnPath(null, "node"));
+ assertThrows(IOException.class, () -> ComplexJSTypesGenerator.resolveExecutableOnPath(" ", "node"));
+ assertThrows(IOException.class, () -> ComplexJSTypesGenerator.resolveExecutableOnPath("relative-bin", "node"));
+ }
}