This repository has been archived on 2026-11-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
carl/scripts/e2e/CarlPublicHouseholdFixtureTest.java
james.maes 1b334d1c03 feat: build Carl family agent and financial workflows
Implement the standalone Carl application, native administration, family API, financial workflows, protected reports, public fictional fixtures and consistent monetary presentation. Fresh full verification passes 419 tests; running package payloads match. Record the owner-confirmed CI/CD and GitOps direction for the next implementation.
2026-10-01 14:25:52 -05:00

127 lines
11 KiB
Java

/* Copyright (C) 2026 KofTwentyTwo */
package com.kof22.carlai.domain;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Clock;
import java.time.LocalDate;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.testcontainers.containers.PostgreSQLContainer;
import static org.junit.jupiter.api.Assertions.*;
/** Actual PostgreSQL/service acceptance for the public fixture, including reimport/correction/privacy mutations. */
public final class CarlPublicHouseholdFixtureTest
{
private CarlPublicHouseholdFixtureTest() {}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception
{
if(args.length != 1) throw new IllegalArgumentException("Exactly one public fixture directory required");
Path directory = Path.of(args[0]);
try(var database = new PostgreSQLContainer<>("postgres:16-alpine"))
{
database.start();
var source = com.kof22.agentadmin.bootstrap.NativeDatabases.source(database.getJdbcUrl(), database.getUsername(), database.getPassword());
com.kof22.agentcore.store.AgentMigrations.migrate(source);
var service = new CarlService(source, Clock.systemUTC());
service.transaction(c ->
{
CarlService.execute(c, "INSERT INTO carl_household(id,name,display_zone) VALUES(1,'Synthetic Household','America/Chicago')");
CarlService.execute(c, "INSERT INTO carl_member(id,household_id,principal,label,can_manage) VALUES(1,1,'alice','Fictional Aster',true),(2,1,'bob','Fictional Basil',true)");
CarlService.execute(c, "INSERT INTO carl_permission(member_id,domain,details) SELECT m.id,d,true FROM carl_member m CROSS JOIN unnest(ARRAY['BILLS','VENDORS','CALENDAR','FINANCE','TAX','SETTINGS']) d");
return null;
});
for(String input : java.util.List.of("transactions.csv", "balances.csv", "household.json", "calendar.ics", "reminders.ics"))
{
Path changed = Files.createTempDirectory(directory.toAbsolutePath().getParent().getParent().resolve("target"), "public-bundle-corruption-");
for(String name : java.util.List.of("household.json", "transactions.csv", "balances.csv", "calendar.ics", "reminders.ics"))
Files.copy(directory.resolve(name), changed.resolve(name));
var modified = changed.resolve(input);
String before = Files.readString(modified);
if(input.equals("transactions.csv")) before = before.replaceFirst("Fictional independently authored ledger event", "UNAPPROVED_PRIVATE_CANARY");
else if(input.equals("balances.csv")) before = before.replaceFirst("50000.00", "UNAPPROVED_PRIVATE_CANARY");
else if(input.equals("household.json")) before = before.replaceFirst("Fictional Household Cash", "UNAPPROVED_PRIVATE_CANARY");
else before = before.replace("Fictional shared household review", "UNAPPROVED_PRIVATE_CANARY").replace("Fictional review maintenance quote", "UNAPPROVED_PRIVATE_CANARY");
Files.writeString(modified, before);
var denied = assertThrows(SecurityException.class, () -> CarlPublicHouseholdSeed.seed(service, changed));
assertEquals("Canonical public fictional v1 bundle required; modified files cannot enter the fixture", denied.getMessage());
assertEquals(Long.valueOf(0), counts(service).get("records"), "Corrupt input must fail before records");
assertEquals(Long.valueOf(0), service.transaction(c -> CarlService.number(CarlService.rows(c, "SELECT count(*) AS n FROM carl_request").getFirst(), "n")), "Corrupt input must fail before request claims");
}
System.out.println("PUBLIC_FIXTURE_STAGE=seed");
var manifest = CarlPublicHouseholdSeed.seed(service, directory);
var finance = new FinancialRecords(service);
Map<String, Long> accounts = (Map<String, Long>) manifest.get("accountIds");
Map<String, Long> properties = (Map<String, Long>) manifest.get("propertyIds");
var authored = new ObjectMapper().readTree(Files.readString(directory.resolve("household.json")));
var counts = counts(service);
assertEquals(10920L, counts.get("transactions"));
assertEquals(22365L, counts.get("balances"));
assertEquals(36L, counts.get("accounts"));
assertEquals(3L, counts.get("properties"));
assertEquals(3L, counts.get("units"));
assertEquals(4L, counts.get("bills"));
for(var account : accounts.entrySet())
{
if(account.getKey().equals("Fictional Unvalued Collectible")) continue;
var reconciled = finance.reconcile("alice", account.getValue(), LocalDate.of(2024, 12, 31), LocalDate.of(2026, 9, 30));
assertEquals(0, ((BigDecimal) reconciled.get("unexplainedDifference")).signum(), account.getKey());
assertEquals(0, new BigDecimal(authored.path("expected").path("closingByAccount").path(account.getKey()).asText()).compareTo((BigDecimal) reconciled.get("closing")), account.getKey());
}
var shared = new CarlService.Scope("alice", Set.of("alice", "bob"));
var overview = finance.overview(shared, LocalDate.of(2026, 9, 1), LocalDate.of(2026, 9, 30));
assertEquals(0, new BigDecimal("136615.00").compareTo((BigDecimal) ((Map<?, ?>) overview.get("liquidBalancesByCurrency")).get("USD")));
var actualFlows = (Map<String, BigDecimal>) overview.get("classifiedFlows");
assertEquals(authored.path("expected").path("sharedSeptemberFlows").size(), actualFlows.size());
for(var flow : actualFlows.entrySet()) assertEquals(0, new BigDecimal(authored.path("expected").path("sharedSeptemberFlows").path(flow.getKey()).asText()).compareTo(flow.getValue()), flow.getKey());
assertFalse(service.view(shared, "accounts").stream().anyMatch(a -> a.get("id").equals(accounts.get("Fictional Aster Wallet"))));
var homes = new HomeRecords(service);
assertEquals("PRIMARY_RESIDENCE", homes.get("bob", properties.get("home")).get("property_use"));
assertEquals(Integer.valueOf(0), service.transaction(c -> ((Number) CarlService.rows(c, "SELECT count(*) AS n FROM carl_rental_unit WHERE property_id=?", properties.get("home")).getFirst().get("n")).intValue()));
assertEquals(Integer.valueOf(0), service.transaction(c -> ((Number) CarlService.rows(c, "SELECT count(*) AS n FROM carl_rent_due WHERE property_id=?", properties.get("home")).getFirst().get("n")).intValue()));
var arrears = service.transaction(c -> (BigDecimal) CarlService.rows(c, "SELECT sum(d.amount-coalesce((SELECT sum(x.amount) FROM carl_rent_application_view x WHERE x.principal=d.principal AND x.rent_due_id=d.id AND x.active),0)) AS amount FROM carl_rent_due_view d WHERE d.principal='alice'").getFirst().get("amount"));
assertEquals(0, new BigDecimal("250.00").compareTo(arrears));
assertEquals("COMPLETE", service.artifact("bob", ((Number) manifest.get("reportId")).longValue()).get("narration_state"));
assertEquals(Long.valueOf(2), service.transaction(c -> CarlService.number(CarlService.rows(c, "SELECT count(*) AS n FROM carl_artifact_audience WHERE artifact_id=?", manifest.get("reportId")).getFirst(), "n")));
var receipt = CarlPublicHouseholdSeed.seed(service, directory);
assertEquals(new ObjectMapper().readTree(new ObjectMapper().writeValueAsString(manifest)), new ObjectMapper().readTree(new ObjectMapper().writeValueAsString(receipt)));
assertEquals(counts, counts(service), "Successful seed retry must not duplicate artifacts, revisions, balances or accounts");
System.out.println("PUBLIC_FIXTURE_STAGE=dedup-corrections");
var before = counts(service);
finance.importTransactions("alice", UUID.randomUUID(), Files.readString(directory.resolve("case-overlap.csv")), accounts, false);
finance.importTransactions("alice", UUID.randomUUID(), Files.readString(directory.resolve("case-omitted.csv")), accounts, false);
assertEquals(before.get("transactions"), counts(service).get("transactions"), "Overlap does not duplicate; omission never deletes");
assertThrows(IllegalArgumentException.class, () -> finance.importTransactions("alice", UUID.randomUUID(), Files.readString(directory.resolve("case-correction.csv")), accounts, false));
finance.importTransactions("alice", UUID.randomUUID(), Files.readString(directory.resolve("case-correction.csv")), accounts, true);
var corrected = service.transaction(c -> CarlService.rows(c, "SELECT notes,version FROM carl_transaction_source WHERE notes LIKE 'Fictional corrected multiline note%'").getFirst());
assertEquals(2L, ((Number) corrected.get("version")).longValue());
var original = service.transaction(c -> CarlService.rows(c, "SELECT notes FROM carl_transaction_source WHERE external_id=(SELECT external_id FROM carl_transaction_source WHERE notes LIKE 'Fictional corrected multiline note%') AND version=1").getFirst());
assertEquals("Fictional independently authored ledger event", original.get("notes"));
assertThrows(IllegalArgumentException.class, () -> finance.importTransactions("alice", UUID.randomUUID(), Files.readString(directory.resolve("case-missingAmount.csv")), accounts, false));
assertThrows(IllegalArgumentException.class, () -> finance.importBalances("alice", UUID.randomUUID(), Files.readString(directory.resolve("case-balanceConflict.csv")), accounts, false));
assertEquals(before.get("transactions"), counts(service).get("transactions"));
service.transaction(c -> { CarlService.execute(c, "UPDATE carl_permission SET details=false WHERE member_id=2 AND domain='FINANCE'"); return null; });
assertThrows(SecurityException.class, () -> homes.get("bob", properties.get("home")));
assertThrows(SecurityException.class, () -> homes.history("bob", properties.get("home")));
assertThrows(SecurityException.class, () -> service.artifact("bob", ((Number) manifest.get("balanceId")).longValue()));
service.transaction(c -> { CarlService.execute(c, "UPDATE carl_household SET name='UNPREPARED TARGET' WHERE id=1"); return null; });
assertThrows(SecurityException.class, () -> CarlPublicHouseholdSeed.seed(service, directory));
System.out.println("PUBLIC_FIXTURE_PASS=" + new ObjectMapper().writeValueAsString(Map.of("mode", "controlled", "providerCalls", 0, "counts", before, "reconciledAccounts", 35, "privateSharedAndRevocation", "PASS", "duplicateSeed", "PASS", "overlapCorrectionConflict", "PASS")));
}
}
private static Map<String, Long> counts(CarlService service)
{
return service.transaction(c ->
{
var result = new java.util.LinkedHashMap<String, Long>();
for(var entry : Map.of("records", "carl_record", "accounts", "carl_account", "transactions", "carl_transaction", "balances", "carl_balance", "properties", "carl_property", "units", "carl_rental_unit", "bills", "carl_bill", "artifacts", "carl_artifact", "corrections", "carl_correction").entrySet())
result.put(entry.getKey(), CarlService.number(CarlService.rows(c, "SELECT count(*) AS n FROM " + entry.getValue()).getFirst(), "n"));
return result;
});
}
}