Skip to content

fix: persist keyword changes reliably in backend - #72

Merged
Benevanio merged 1 commit into
developfrom
feature/vps-secure-integration
Apr 7, 2026
Merged

Benevanio merged 1 commit into
developfrom
feature/vps-secure-integration

Conversation

@Benevanio

Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings April 7, 2026 15:40
@Benevanio Benevanio self-assigned this Apr 7, 2026
@vercel

vercel Bot commented Apr 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
painel-vagas Ready Ready Preview, Comment Apr 7, 2026 3:40pm

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves how the backend persists and reads keyword configuration by making the keywords file path configurable and resolving it reliably (independent of process.cwd()), while also normalizing keyword lists (trim/dedupe/remove blanks) and updating tests for the new behavior.

Changes:

  • Add KEYWORDS_FILE_PATH support and resolve the default keywords file path relative to the module directory.
  • Normalize keyword lists on write/read to ensure consistent persisted and returned values (including allowing an intentionally empty list).
  • Update unit/integration tests to cover the new env var and empty-list persistence behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
backend/src/jobsApiApp.js Writes/reads keywords using a configurable path, adds normalization, and ensures directories are created when persisting.
backend/src/config.js Reads keywords from the same configurable file path (preferring file over SEARCH_KEYWORDS) and normalizes file-provided keywords.
backend/tests/unit/utils/config.test.js Updates config tests to set KEYWORDS_FILE_PATH and adds a case for preserving an existing empty keyword list.
backend/tests/integration/jobsApi.test.js Resets KEYWORDS_FILE_PATH between tests and adds a POST case for creating a nested keywords file and accepting an empty list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/jobsApiApp.js

try {
const data = JSON.parse(readFileSync(envPath, "utf-8"));
return data && typeof data === "object" ? data : { KEYWORDS: [] };

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readEnvironmentData() treats any parsed JSON with typeof === "object" as valid, which includes arrays. If the file accidentally contains [], the function will return an array and later POST spreads it into an object (adding numeric keys) before writing back. Consider rejecting arrays explicitly (e.g., require a plain object) so corrupted/invalid shapes don't produce surprising persisted output.

Suggested change
return data && typeof data === "object" ? data : { KEYWORDS: [] };
return data && typeof data === "object" && !Array.isArray(data) ? data : { KEYWORDS: [] };

Copilot uses AI. Check for mistakes.
Comment thread backend/src/jobsApiApp.js
Comment on lines +20 to +33
function getKeywordsFilePath() {
const configuredPath = process.env.KEYWORDS_FILE_PATH?.trim();
return configuredPath
? path.resolve(configuredPath)
: path.resolve(MODULE_DIR, "db", "environment.json");
}

function normalizeKeywords(keywords) {
if (!Array.isArray(keywords)) {
return null;
}

return [...new Set(keywords.map((item) => String(item ?? "").trim()).filter(Boolean))];
}

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getKeywordsFilePath()/normalizeKeywords() are duplicated here and in src/config.js. Because both the API and getConfig() must agree on the exact keywords file path and normalization rules, duplicating the logic risks future drift (writing to one path while reading from another). Consider extracting these helpers into a shared module that both files import.

Copilot uses AI. Check for mistakes.
Comment on lines +184 to +199
it("POST /api/keywords cria o arquivo configurado e aceita lista vazia", async () => {
tmpDir = mkdtempSync(join(tmpdir(), "jobs-api-"));
process.env.KEYWORDS_FILE_PATH = join(tmpDir, "nested", "environment.json");

const app = createJobsApiApp({ outputDir: tmpDir });
const res = await request(app)
.post("/api/keywords")
.send({ keywords: [" ", ""] })
.expect(200);

expect(res.body).toEqual({
ok: true,
message: "Keywords atualizadas com sucesso.",
keywords: [],
});
});

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new test says it "cria o arquivo configurado", but it only asserts the HTTP response body. To actually validate the behavior the PR is targeting, add assertions that the file at KEYWORDS_FILE_PATH was created (including nested directories) and that its JSON content contains KEYWORDS: [] after normalization.

Copilot uses AI. Check for mistakes.
Comment on lines 51 to +55
it("parseia SEARCH_KEYWORDS em lista", () => {
vi.stubEnv("SEARCH_KEYWORDS", "Java","Spring","RabbitMQ","Docker");
const tempDir = mkdtempSync(path.join(tmpdir(), "jobs-config-"));
vi.stubEnv("KEYWORDS_FILE_PATH", path.join(tempDir, "missing-environment.json"));
vi.stubEnv("SEARCH_KEYWORDS", "Java,Spring,RabbitMQ,Docker");

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests create temporary directories via mkdtempSync but never remove them. Over time (especially in local runs), this can leave many stray jobs-config-* folders in the OS temp dir. Consider tracking the created tempDir/keywordsFile per test and cleaning it up in afterEach (e.g., rmSync(tempDir, { recursive: true, force: true })).

Copilot uses AI. Check for mistakes.
Comment thread backend/src/config.js
Comment on lines +41 to +54
function getKeywordsFilePath() {
const configuredPath = process.env.KEYWORDS_FILE_PATH?.trim();
return configuredPath
? path.resolve(configuredPath)
: path.resolve(MODULE_DIR, "db", "environment.json");
}

function normalizeKeywords(keywords) {
if (!Array.isArray(keywords)) {
return null;
}

return [...new Set(keywords.map((item) => String(item ?? "").trim()).filter(Boolean))];
}

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getKeywordsFilePath()/normalizeKeywords() are duplicated here and in src/jobsApiApp.js. Since the API writes the keywords file and getConfig() reads it, duplicating the path/normalization logic risks divergence and hard-to-debug persistence issues. Consider extracting a shared helper module to keep these rules in one place.

Copilot uses AI. Check for mistakes.
@Benevanio
Benevanio merged commit 9071ee2 into develop Apr 7, 2026
7 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in JobAtlas – Kanban Apr 7, 2026

This branch was previously deployed

1 inactive deployment
Preview — 4e0b56fd Deployed Apr 7, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants