Completar combinação CBF15 com saldos anteriores e conferir PEC residual
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-11-11 07:35:04 +02:00
parent 67cd583b94
commit 9c067d6ae8
10 changed files with 357 additions and 18 deletions
@@ -1,3 +1,13 @@
## 11/11/2026 — combinação CBF15 com créditos económicos/internacionais e PEC em QA
Retoma da ordem de conclusão existente, sem nova auditoria ou repetição da publicação financeira:270instituições publicadas e revisão instalada4f3dbcf8 preservadas; visual financeiro/S08 permanece separado. Reconciliação delimitada: o transporte CBF15 básico(7476dc34) já integra a revisão instalada4f3dbcf8, pelo histórico Git e recibos aceites; a nota antiga «QA, não instalado» desse transporte foi superada por entregas posteriores. Não reinstalar esse recorte como se estivesse ausente.
**Implementado nesta passagem, ainda sem instalação:** retirada a recusa técnica genérica da combinação de CBF15 corrente/transportado com saldos anteriores, reutilizando as fontes e ordem de dedução existentes. Créditos económicos e internacionais/correspondentes transportes precedemCBF15; PEC corrente e anterior usam apenas a colecta residual seguinte, antes de retenções/PPC. Uma escolha explícita de PEC superior ao residual é recusada como indeterminada, sem reduzir silenciosamente parcelas ou fabricar saldo recuperável. Histórico e fontes de investimento/uso anterior continuam preservados; a chave da combinação inclui versão própria para exigir recálculo dos resultados anteriores desse ramo, sem reescrever históricos nem invalidar os recortes sem combinação.
Cinco cenários sintéticos: combinação ordinária138MZN; quota residual documental100→saldo438; PEC sobrealocado→saldo indeterminado; CBF15 esgota colecta1338 ePEC seleccionado expressamente0→saldo−500 proveniente dePPC, sem reembolso/consumo oficial; janelaCBF15 expirada→saldo538. Também provada a varianteCBF15 corrente e o limite de quota após créditos anteriores. Cálculo→POST/PUT do rascunho privado existente→GET→recálculo exacto→HTML/dossier aprovados no QA55441/proactive_investment_20261027_test, com rollback e sem novas bases/portas/migrations/direitos. Uma provaAPI/37asserções; cinco testes novos nas combinações e23casos afectados de compatibilidade aprovados, não multiplicados como funcionalidades. Tipos/lint aprovados; compilação QA e pacote registados no fecho. Provas `outputs/irpc-cbf15-combination-20261111/*-verification.json` e relatórios recuperados.
**Saldo:** implementação e percurso desta combinação concluídos emQA; instalação do novo delta e visual separados. Persistem repartição automática/múltiplos projectos, outros períodos/moedas/regimes/EP/cumulações/benefícios e documentos fora dos recortes demonstrados; L1–L7/restanteL8/L9–L16 não são encerrados. Os cinco programas anteriores privados do directório financeiro não são publicados por consequência desta entrega. Próximo trabalho: agrupar a entrega compatível deste delta e continuar o primeiro residual fiscal executável, preservando os fechos aceites.
## 10/11/2026 —270instituições publicadas e consultáveis na homologação
**Cadastro e disponibilização do documento fornecido concluídos em homologação.** Conferência de leitura às22:54:31Africa/Johannesburg(20:54:31UTC), revisão instalada `4f3dbcf86499180011fc79712d6b5be445e57e26`:270/270instituições/operadores do documento publicados, nenhuma referência do original em falta,270identidades únicas,393referências físicas e168contactos. Publicações confirmadas pelo fluxo editorial nativo do utilizador; zero escritas efectuadas pelo verificador. As275fichas totais incluem cinco referências anteriores de fundos/programas que permanecem privadas, sem serem publicadas por consequência deste lote. Fonte original/SHA/edição, incertezas e identidades preservadas.
@@ -1,3 +1,13 @@
## 11/11/2026 — combinação CBF15 com créditos económicos/internacionais e PEC em QA
Retoma da ordem de conclusão existente, sem nova auditoria ou repetição da publicação financeira:270instituições publicadas e revisão instalada4f3dbcf8 preservadas; visual financeiro/S08 permanece separado. Reconciliação delimitada: o transporte CBF15 básico(7476dc34) já integra a revisão instalada4f3dbcf8, pelo histórico Git e recibos aceites; a nota antiga «QA, não instalado» desse transporte foi superada por entregas posteriores. Não reinstalar esse recorte como se estivesse ausente.
**Implementado nesta passagem, ainda sem instalação:** retirada a recusa técnica genérica da combinação de CBF15 corrente/transportado com saldos anteriores, reutilizando as fontes e ordem de dedução existentes. Créditos económicos e internacionais/correspondentes transportes precedemCBF15; PEC corrente e anterior usam apenas a colecta residual seguinte, antes de retenções/PPC. Uma escolha explícita de PEC superior ao residual é recusada como indeterminada, sem reduzir silenciosamente parcelas ou fabricar saldo recuperável. Histórico e fontes de investimento/uso anterior continuam preservados; a chave da combinação inclui versão própria para exigir recálculo dos resultados anteriores desse ramo, sem reescrever históricos nem invalidar os recortes sem combinação.
Cinco cenários sintéticos: combinação ordinária138MZN; quota residual documental100→saldo438; PEC sobrealocado→saldo indeterminado; CBF15 esgota colecta1338 ePEC seleccionado expressamente0→saldo−500 proveniente dePPC, sem reembolso/consumo oficial; janelaCBF15 expirada→saldo538. Também provada a varianteCBF15 corrente e o limite de quota após créditos anteriores. Cálculo→POST/PUT do rascunho privado existente→GET→recálculo exacto→HTML/dossier aprovados no QA55441/proactive_investment_20261027_test, com rollback e sem novas bases/portas/migrations/direitos. Uma provaAPI/37asserções; cinco testes novos nas combinações e23casos afectados de compatibilidade aprovados, não multiplicados como funcionalidades. Tipos/lint aprovados; compilação QA e pacote registados no fecho. Provas `outputs/irpc-cbf15-combination-20261111/*-verification.json` e relatórios recuperados.
**Saldo:** implementação e percurso desta combinação concluídos emQA; instalação do novo delta e visual separados. Persistem repartição automática/múltiplos projectos, outros períodos/moedas/regimes/EP/cumulações/benefícios e documentos fora dos recortes demonstrados; L1–L7/restanteL8/L9–L16 não são encerrados. Os cinco programas anteriores privados do directório financeiro não são publicados por consequência desta entrega. Próximo trabalho: agrupar a entrega compatível deste delta e continuar o primeiro residual fiscal executável, preservando os fechos aceites.
## 10/11/2026 —270instituições publicadas e consultáveis na homologação
**Cadastro e disponibilização do documento fornecido concluídos em homologação.** Conferência de leitura às22:54:31Africa/Johannesburg(20:54:31UTC), revisão instalada `4f3dbcf86499180011fc79712d6b5be445e57e26`:270/270instituições/operadores do documento publicados, nenhuma referência do original em falta,270identidades únicas,393referências físicas e168contactos. Publicações confirmadas pelo fluxo editorial nativo do utilizador; zero escritas efectuadas pelo verificador. As275fichas totais incluem cinco referências anteriores de fundos/programas que permanecem privadas, sem serem publicadas por consequência deste lote. Fonte original/SHA/edição, incertezas e identidades preservadas.
File diff suppressed because one or more lines are too long
@@ -1,3 +1,13 @@
## 11/11/2026 — combinação CBF15 com créditos económicos/internacionais e PEC em QA
Retoma da ordem de conclusão existente, sem nova auditoria ou repetição da publicação financeira:270instituições publicadas e revisão instalada4f3dbcf8 preservadas; visual financeiro/S08 permanece separado. Reconciliação delimitada: o transporte CBF15 básico(7476dc34) já integra a revisão instalada4f3dbcf8, pelo histórico Git e recibos aceites; a nota antiga «QA, não instalado» desse transporte foi superada por entregas posteriores. Não reinstalar esse recorte como se estivesse ausente.
**Implementado nesta passagem, ainda sem instalação:** retirada a recusa técnica genérica da combinação de CBF15 corrente/transportado com saldos anteriores, reutilizando as fontes e ordem de dedução existentes. Créditos económicos e internacionais/correspondentes transportes precedemCBF15; PEC corrente e anterior usam apenas a colecta residual seguinte, antes de retenções/PPC. Uma escolha explícita de PEC superior ao residual é recusada como indeterminada, sem reduzir silenciosamente parcelas ou fabricar saldo recuperável. Histórico e fontes de investimento/uso anterior continuam preservados; a chave da combinação inclui versão própria para exigir recálculo dos resultados anteriores desse ramo, sem reescrever históricos nem invalidar os recortes sem combinação.
Cinco cenários sintéticos: combinação ordinária138MZN; quota residual documental100→saldo438; PEC sobrealocado→saldo indeterminado; CBF15 esgota colecta1338 ePEC seleccionado expressamente0→saldo−500 proveniente dePPC, sem reembolso/consumo oficial; janelaCBF15 expirada→saldo538. Também provada a varianteCBF15 corrente e o limite de quota após créditos anteriores. Cálculo→POST/PUT do rascunho privado existente→GET→recálculo exacto→HTML/dossier aprovados no QA55441/proactive_investment_20261027_test, com rollback e sem novas bases/portas/migrations/direitos. Uma provaAPI/37asserções; cinco testes novos nas combinações e23casos afectados de compatibilidade aprovados, não multiplicados como funcionalidades. Tipos/lint aprovados; compilação QA e pacote registados no fecho. Provas `outputs/irpc-cbf15-combination-20261111/*-verification.json` e relatórios recuperados.
**Saldo:** implementação e percurso desta combinação concluídos emQA; instalação do novo delta e visual separados. Persistem repartição automática/múltiplos projectos, outros períodos/moedas/regimes/EP/cumulações/benefícios e documentos fora dos recortes demonstrados; L1–L7/restanteL8/L9–L16 não são encerrados. Os cinco programas anteriores privados do directório financeiro não são publicados por consequência desta entrega. Próximo trabalho: agrupar a entrega compatível deste delta e continuar o primeiro residual fiscal executável, preservando os fechos aceites.
## 10/11/2026 —270instituições publicadas e consultáveis na homologação
**Cadastro e disponibilização do documento fornecido concluídos em homologação.** Conferência de leitura às22:54:31Africa/Johannesburg(20:54:31UTC), revisão instalada `4f3dbcf86499180011fc79712d6b5be445e57e26`:270/270instituições/operadores do documento publicados, nenhuma referência do original em falta,270identidades únicas,393referências físicas e168contactos. Publicações confirmadas pelo fluxo editorial nativo do utilizador; zero escritas efectuadas pelo verificador. As275fichas totais incluem cinco referências anteriores de fundos/programas que permanecem privadas, sem serem publicadas por consequência deste lote. Fonte original/SHA/edição, incertezas e identidades preservadas.
+20 -12
View File
@@ -565,6 +565,9 @@ function key(d: Record<string, unknown>, b: AnnualBook) {
acquisitionScope(d),
input,
dependencies(d, !!b.investmentCredit),
...(b.investmentCredit && b.creditCarry?.lots.length
? ['CBF15-credit-carry-combination/1']
: []),
]);
}
export function annualCurrent(d: Record<string, unknown>, b: AnnualBook) {
@@ -1303,16 +1306,6 @@ export function calculateAnnual(d: Record<string, unknown>, b: AnnualBook) {
if (b.investmentCredit && !investmentPreview)
pending.push('CréditoCBF15 exige colecta L6 recuperável.');
if (investmentPreview) pending.push(...investmentPreview.pending);
if (
investmentPreview?.available &&
cmp(investmentPreview.available, zero()) > 0n &&
(!investmentPreview.projectCeiling ||
cmp(investmentPreview.projectCeiling, zero()) > 0n) &&
b.creditCarry?.lots.length
)
pending.push(
'CombinaçãoCBF15 com saldos anteriores exige correspondência própria; transportes anteriores preservados.',
);
if (
b.investmentCredit?.qualification.state === 'known' &&
b.investmentCredit.qualification.eligible === 'yes' &&
@@ -1462,7 +1455,7 @@ export function calculateAnnual(d: Record<string, unknown>, b: AnnualBook) {
)
: null;
if (carry) pending.push(...carry.pending);
if (investmentPreview?.projectCeiling && !pending.length) {
if (investmentPreview && !pending.length) {
const beforeInternational = economicPreview?.afterCredit ?? colecta;
const preview = b.internationalCredits
? internationalDeduction(
@@ -1478,10 +1471,25 @@ export function calculateAnnual(d: Record<string, unknown>, b: AnnualBook) {
carry.priorInternational,
)
: (preview?.afterCredit ?? beforeInternational);
if (!remaining || cmp(investmentPreview.projectCeiling, remaining) > 0n)
if (
investmentPreview.projectCeiling &&
(!remaining || cmp(investmentPreview.projectCeiling, remaining) > 0n)
)
pending.push(
'Quota residualCBF15 do projecto excede a colecta após créditos económicos/internacionais; reconcilie a atribuição antes de deduzir.',
);
if (!pending.length && carry?.pec && remaining) {
const afterInvestment = deductInvestmentCredit(
investmentPreview, remaining, true,
).afterCredit;
if (
afterInvestment &&
cmp(addFractions([carry.pec, carry.priorPec]), afterInvestment) > 0n
)
pending.push(
'As parcelas de PEC corrente e anterior excedem a colecta residual após CBF15; reconcilie a escolha documentada sem reduzir valores automaticamente.',
);
}
}
const ready = pending.length === 0 && colecta !== null,
economic = b.economicCredits
@@ -0,0 +1,46 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\TestCase;
final class IrpcInvestmentCombinationDraftTest extends TestCase
{
use DatabaseTransactions;
public function test_combination_and_explicit_pe_c_reconciliation_survive_private_draft_recovery(): void
{
$target = DB::selectOne('select current_database() as name,inet_server_port() as port');
$this->assertSame('proactive_investment_20261027_test', $target->name);
$this->assertSame(55441, (int) $target->port);
config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]);
$this->actingAs(User::findOrFail(1021));
$id = null;
$etag = null;
foreach (['combined' => 138, 'quota' => 438, 'overallocated' => null, 'reconciled' => -500, 'expired' => 538] as $name => $balance) {
$snapshot = json_decode(file_get_contents(base_path('outputs/irpc-cbf15-combination-20261111/'.$name.'.json')), true, flags: JSON_THROW_ON_ERROR);
$personal = ['person_type' => $snapshot['person_type']];
unset($snapshot['person_type']);
$payload = ['personal_snapshot' => $personal, 'economic_snapshot' => $snapshot];
if ($id === null) {
$id = $this->postJson('/api/v1/simulation-drafts', $payload, ['Idempotency-Key' => (string) Str::uuid()])->assertCreated()->json('data.id');
} else {
$this->putJson('/api/v1/simulation-drafts/'.$id, $payload, ['If-Match' => $etag])->assertOk();
}
$response = $this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk();
$etag = $response->headers->get('ETag');
$back = $response->json('data.economic_snapshot');
$this->assertSame($snapshot['modelo_a_snapshot'], $back['modelo_a_snapshot']);
$book = json_decode($back['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR)['irpcAnnual'][$snapshot['context_opportunity']];
$this->assertCount(1, $book['history']);
$this->assertSame(['numerator' => '138', 'denominator' => '1'], $book['history'][0]['model']['arithmeticBalance']);
$this->assertSame($balance === null ? null : ['numerator' => (string) $balance, 'denominator' => '1'], $book['result']['model']['arithmeticBalance']);
$this->assertFalse($book['result']['model']['investmentCredit']['officialBalancesConsumed']);
file_put_contents(base_path('outputs/irpc-cbf15-combination-20261111/recovered-'.$name.'.json'), json_encode($back, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
}
}
}
@@ -0,0 +1,14 @@
import { economicCase } from "./irpc-economic-case.mjs";
import { investmentCarryCase } from "./irpc-investment-carry-case.mjs";
import {
prepareInvestmentCarry,
} from "../../../resources/js/lib/irpc-annual-settlement.ts";
export function combinedCase() {
const c = economicCase(true);
c.b = prepareInvestmentCarry(c.d, c.b);
c.b.investmentCredit = structuredClone(
investmentCarryCase().b.investmentCredit,
);
c.b.investmentCredit.assessmentKey = c.b.assessmentKey;
return c;
}
@@ -0,0 +1,101 @@
import test from "node:test";
import assert from "node:assert/strict";
import { investmentCase } from "./helpers/irpc-investment-case.mjs";
import { combinedCase } from "./helpers/irpc-investment-combination-case.mjs";
import {
calculateAnnual,
recordAnnual,
annualCurrent,
annualBook,
saveAnnualBook,
annualHtml,
} from "../../resources/js/lib/irpc-annual-settlement.ts";
const f = (n) => ({ numerator: String(n), denominator: "1" });
test("CBF15 carry combines with economic/current and prior international/PEC once in statutory order", () => {
const c = combinedCase(),
m = calculateAnnual(c.d, c.b);
assert.deepEqual(m.pending, []);
assert.deepEqual(m.investmentCredit.deducted, f(400));
assert.deepEqual(m.arithmeticBalance, f(138));
});
test("explicit current and prior PEC allocation cannot exceed collecta left after CBF15", () => {
const c = combinedCase();
Object.assign(c.b.investmentCredit.assets[0], {
amount: "40000",
proofAmount: "40000",
});
const m = calculateAnnual(c.d, c.b);
assert.equal(
m.arithmeticBalance,
null,
"An excessive PEC allocation must not manufacture a recoverable balance",
);
assert.match(m.pending.join(" "), /PEC.*CBF15/);
});
test("documented residual quota and expired original window retain distinct unused credits", () => {
const c = combinedCase();
c.b.investmentCredit.soleProject = "no";
c.b.investmentCredit.projectQuota = {
amount: "100",
reference:
"SYNTHETIC residual collecta allocated to this project after prior credits",
};
let m = calculateAnnual(c.d, c.b);
assert.deepEqual(m.pending, []);
assert.deepEqual(m.arithmeticBalance, f(438));
assert.deepEqual(m.investmentCredit.unused, f(300));
c.b.investmentCredit.projectQuota.amount = "1339";
assert.match(
calculateAnnual(c.d, c.b).pending.join(" "),
/Quota residualCBF15/,
);
c.b.investmentCredit.soleProject = "yes";
c.b.investmentCredit.startDate = "2021-01-01";
m = calculateAnnual(c.d, c.b);
assert.deepEqual(m.pending, []);
assert.deepEqual(m.investmentCredit.expired, f(400));
assert.deepEqual(m.arithmeticBalance, f(538));
});
test("explicit zero PEC allocation after exhausted collecta preserves prior PEC and recovers only PPC", () => {
const c = combinedCase();
Object.assign(c.b.investmentCredit.assets[0], {
amount: "40000",
proofAmount: "40000",
});
c.b.creditCarry.currentPec = "0";
c.b.creditCarry.lots[1].selected = "0";
const m = calculateAnnual(c.d, c.b);
assert.deepEqual(m.pending, []);
assert.deepEqual(m.investmentCredit.deducted, f(1338));
assert.deepEqual(m.pecDeduction.deducted, f(0));
assert.deepEqual(m.creditCarry.priorPec, f(0));
assert.deepEqual(m.arithmeticBalance, f(-500));
assert.equal(m.creditCarry.officialBalancesConsumed, false);
const b = recordAnnual(c.d, c.b, "2026-11-11T09:00:00+02:00"),
d = { ...c.d, modelo_a_snapshot: saveAnnualBook(c.d, b) },
back = annualBook(d);
assert.equal(annualCurrent(d, back), true);
assert.deepEqual(
calculateAnnual(d, back).investmentCredit,
m.investmentCredit,
);
assert.match(annualHtml(d), /CBF15/);
});
test("current CBF15 and prior international/PEC obey the same residual collecta guard", () => {
const c = combinedCase();
c.b.version = 2;
c.b.investmentCredit = structuredClone(investmentCase().b.investmentCredit);
c.b.investmentCredit.assessmentKey = c.b.assessmentKey;
let m = calculateAnnual(c.d, c.b);
assert.deepEqual(m.pending, []);
assert.deepEqual(m.arithmeticBalance, f(38));
Object.assign(c.b.investmentCredit.assets[0], {
amount: "40000",
proofAmount: "40000",
});
m = calculateAnnual(c.d, c.b);
assert.equal(m.arithmeticBalance, null);
assert.match(m.pending.join(" "), /PEC.*CBF15/);
});
+114
View File
@@ -0,0 +1,114 @@
import fs from "node:fs";
import assert from "node:assert/strict";
import { combinedCase } from "../tests/frontend/helpers/irpc-investment-combination-case.mjs";
import {
calculateAnnual,
recordAnnual,
annualBook,
annualCurrent,
saveAnnualBook,
annualHtml,
} from "../resources/js/lib/irpc-annual-settlement.ts";
import { createProjectDossierReport } from "../resources/js/lib/project-dossier-report.ts";
const out = new URL(
"../outputs/irpc-cbf15-combination-20261111/",
import.meta.url,
);
fs.mkdirSync(out, { recursive: true });
const expected = {
combined: 138,
quota: 438,
overallocated: null,
reconciled: -500,
expired: 538,
};
for (const [name, balance] of Object.entries(expected)) {
if (process.argv[2] === "recovered") {
const d = JSON.parse(
fs.readFileSync(
new URL("recovered-" + name + ".json", out),
"utf8",
),
),
b = annualBook(d),
m = calculateAnnual(d, b);
assert.equal(annualCurrent(d, b), true);
assert.deepEqual(m, b.result.model);
assert.deepEqual(
m.arithmeticBalance,
balance === null
? null
: { numerator: String(balance), denominator: "1" },
);
const html = annualHtml(d),
report = createProjectDossierReport(d, {
selectedSections: new Set(["finance"]),
generatedAt: "2026-11-11T09:00:00+02:00",
});
assert.match(html, /CBF15/);
assert.match(JSON.stringify(report), /CBF15/);
fs.writeFileSync(new URL(name + "-report.html", out), html);
fs.writeFileSync(
new URL(name + "-verification.json", out),
JSON.stringify(
{
synthetic: true,
apiRecovered: true,
exactRecalculation: true,
html: true,
dossierReport: true,
balance: m.arithmeticBalance,
pending: m.pending,
investment: m.investmentCredit,
carry: m.creditCarry,
officialBalancesConsumed: false,
activeChanged: false,
},
null,
2,
),
);
} else {
const c = combinedCase();
c.b.history = [
recordAnnual(c.d, structuredClone(c.b), "2026-11-11T08:00:00+02:00")
.result,
];
if (name === "quota") {
c.b.investmentCredit.soleProject = "no";
c.b.investmentCredit.projectQuota = {
amount: "100",
reference: "SYNTHETIC documented residual project quota100",
};
}
if (["overallocated", "reconciled"].includes(name))
Object.assign(c.b.investmentCredit.assets[0], {
amount: "40000",
proofAmount: "40000",
});
if (name === "reconciled") {
c.b.creditCarry.currentPec = "0";
c.b.creditCarry.lots[1].selected = "0";
}
if (name === "expired") c.b.investmentCredit.startDate = "2021-01-01";
const book = recordAnnual(c.d, c.b, "2026-11-11T09:00:00+02:00");
assert.deepEqual(
book.result.model.arithmeticBalance,
balance === null
? null
: { numerator: String(balance), denominator: "1" },
);
fs.writeFileSync(
new URL(name + ".json", out),
JSON.stringify({
...c.d,
modelo_a_snapshot: saveAnnualBook(c.d, book),
}),
);
}
}
console.log(
process.argv[2] === "recovered"
? "Five API-recovered combinations recalculated and reported."
: "Five synthetic combinations prepared.",
);
+26
View File
@@ -0,0 +1,26 @@
<?php
$root = dirname(__DIR__);
if (($argv[1] ?? '') !== 'test' || basename($root) !== 'portal-investidor') {
throw new RuntimeException('Explicit local test operation required.');
}
foreach (['APP_ENV' => 'testing', 'APP_KEY' => 'base64:'.base64_encode(str_repeat('Q', 32)),
'APP_CONFIG_CACHE' => $root.'/outputs/irpc-cbf15-combination-20261111/no-config.php',
'DB_CONNECTION' => 'pgsql', 'DB_HOST' => '127.0.0.1', 'DB_PORT' => '55441',
'DB_DATABASE' => 'proactive_investment_20261027_test', 'DB_USERNAME' => 'investmoz_test', 'DB_PASSWORD' => '',
'DB_URL' => '', 'DATABASE_URL' => '', 'CACHE_STORE' => 'array', 'SESSION_DRIVER' => 'array',
'QUEUE_CONNECTION' => 'sync', 'MAIL_MAILER' => 'array', 'SUITECRM_ENABLED' => 'false'] as $key => $value) {
putenv($key.'='.$value);
$_ENV[$key] = $_SERVER[$key] = $value;
}
$pdo = new PDO('pgsql:host=127.0.0.1;port=55441;dbname=proactive_investment_20261027_test', 'investmoz_test', '');
$identity = $pdo->query('select current_database() as name,inet_server_port() as port')->fetch(PDO::FETCH_ASSOC);
if ($identity['name'] !== 'proactive_investment_20261027_test' || (int) $identity['port'] !== 55441) {
throw new RuntimeException('Isolated QA identity required.');
}
$argv = ['phpunit', '-c', 'phpunit.investmoz.xml', '--do-not-cache-result', 'tests/Feature/IrpcInvestmentCombinationDraftTest.php'];
$argc = count($argv);
$_SERVER['argv'] = $argv;
$_SERVER['argc'] = $argc;
require $root.'/vendor/autoload.php';
require $root.'/vendor/phpunit/phpunit/phpunit';