diff --git a/src/main/java/dev/robocode/rumble/client/GitHubIssueOpsTransport.java b/src/main/java/dev/robocode/rumble/client/GitHubIssueOpsTransport.java index 56772f9..45c7e7b 100644 --- a/src/main/java/dev/robocode/rumble/client/GitHubIssueOpsTransport.java +++ b/src/main/java/dev/robocode/rumble/client/GitHubIssueOpsTransport.java @@ -51,7 +51,7 @@ public SubmittedBatch createIssue(final URI repository, final String body, final } @Override - public List receipts(final URI repository, final SubmittedBatch batch) throws IOException { + public SubmissionStatus status(final URI repository, final SubmittedBatch batch) throws IOException { final JsonArray comments = request("GET", endpoint(repository, "issues/" + batch.issueNumber() + "/comments?per_page=100"), null).getAsJsonArray("comments"); final List receipts = new ArrayList<>(); @@ -61,7 +61,9 @@ public List receipts(final URI repository, final SubmittedBat receipts.addAll(receipts(body.getAsString(), batch.issueUrl())); } } - return receipts; + final JsonObject issue = request("GET", endpoint(repository, "issues/" + batch.issueNumber()), null); + final boolean terminal = "closed".equals(issue.get("state").getAsString()); + return new SubmissionStatus(batch, receipts, terminal); } private JsonObject request(final String method, final URI endpoint, final String body) throws IOException { diff --git a/src/main/java/dev/robocode/rumble/client/IssueOpsSubmission.java b/src/main/java/dev/robocode/rumble/client/IssueOpsSubmission.java index b47306e..5bae0b0 100644 --- a/src/main/java/dev/robocode/rumble/client/IssueOpsSubmission.java +++ b/src/main/java/dev/robocode/rumble/client/IssueOpsSubmission.java @@ -31,9 +31,13 @@ final class IssueOpsSubmission { } SubmissionReport submit(final RankedJournal journal, final RumbleSnapshot snapshot) throws IOException { - final List receipts = collectReceipts(journal.unacknowledgedSubmissions(), snapshot); + final List statuses = collectStatuses(journal.unacknowledgedSubmissions(), snapshot); + final List receipts = statuses.stream().flatMap(status -> status.receipts().stream()).toList(); journal.acknowledge(receipts); + final Set terminal = statuses.stream().filter(SubmissionStatus::terminal) + .flatMap(status -> status.batch().battleIds().stream()).collect(Collectors.toSet()); final Set inFlight = journal.unacknowledgedSubmissions().stream().flatMap(batch -> batch.battleIds().stream()) + .filter(battleId -> !terminal.contains(battleId)) .collect(Collectors.toSet()); final List pending = journal.pending().stream().filter(record -> !inFlight.contains(record.battleId())) .toList(); @@ -47,18 +51,16 @@ SubmissionReport submit(final RankedJournal journal, final RumbleSnapshot snapsh return new SubmissionReport(receipts, submitted); } - private List collectReceipts(final List submitted, - final RumbleSnapshot snapshot) throws IOException { - final List receipts = new ArrayList<>(); + private List collectStatuses(final List submitted, + final RumbleSnapshot snapshot) throws IOException { + final List statuses = new ArrayList<>(); for (final SubmittedBatch batch : submitted) { final Set expected = Set.copyOf(batch.battleIds()); - for (final SubmissionReceipt receipt : transport.receipts(snapshot.canonicalDataRepository(), batch)) { - if (expected.contains(receipt.battleId())) { - receipts.add(receipt); - } - } + final SubmissionStatus status = transport.status(snapshot.canonicalDataRepository(), batch); + statuses.add(new SubmissionStatus(batch, status.receipts().stream() + .filter(receipt -> expected.contains(receipt.battleId())).toList(), status.terminal())); } - return receipts; + return statuses; } private static List> batches(final List records) { @@ -110,5 +112,12 @@ record SubmissionReport(List receipts, List s interface IssueOpsTransport { SubmittedBatch createIssue(URI repository, String body, String title) throws IOException; - List receipts(URI repository, SubmittedBatch batch) throws IOException; + SubmissionStatus status(URI repository, SubmittedBatch batch) throws IOException; +} + +/** One observed issue state, including its durable accepted-record receipts. */ +record SubmissionStatus(SubmittedBatch batch, List receipts, boolean terminal) { + SubmissionStatus { + receipts = List.copyOf(receipts); + } } diff --git a/src/test/java/dev/robocode/rumble/client/IssueOpsSubmissionTest.java b/src/test/java/dev/robocode/rumble/client/IssueOpsSubmissionTest.java index c84df5c..392ccaa 100644 --- a/src/test/java/dev/robocode/rumble/client/IssueOpsSubmissionTest.java +++ b/src/test/java/dev/robocode/rumble/client/IssueOpsSubmissionTest.java @@ -60,8 +60,8 @@ public SubmittedBatch createIssue(final URI repository, final String body, final } @Override - public List receipts(final URI repository, final SubmittedBatch batch) { - return List.of(); + public SubmissionStatus status(final URI repository, final SubmittedBatch batch) { + return new SubmissionStatus(batch, List.of(), false); } }; @@ -69,6 +69,24 @@ public List receipts(final URI repository, final SubmittedBat assertEquals(List.of(record), journal.pending()); } + @Test + @Tag("RCL-006") + void testRCL006_IntegrationNegative_terminallyRejectedBatchIsRetried() throws IOException { + final RankedBattleRecord record = record("676b8646-5993-4b60-9a1e-6112530d1e8d"); + final RankedJournal journal = new RankedJournal(temporaryDirectory); + journal.append(record); + journal.recordSubmission(new SubmittedBatch(42, "https://github.com/example/rumble-data/issues/42", + List.of(record.battleId()))); + final TerminalRejectionTransport transport = new TerminalRejectionTransport(record.battleId()); + + final SubmissionReport report = new IssueOpsSubmission(transport, Clock.systemUTC()).submit(journal, snapshot()); + + assertEquals(1, report.submitted().size()); + assertEquals(1, transport.bodies.size()); + assertTrue(transport.bodies.get(0).contains(record.battleId().toString())); + assertEquals(List.of(record), journal.pending()); + } + @Test @Tag("RCL-007") void testRCL007_IntegrationPositive_splitsSixtyOneRecordsAcrossBoundedIssueBatches() throws IOException { @@ -121,8 +139,8 @@ public SubmittedBatch createIssue(final URI repository, final String body, final } @Override - public List receipts(final URI repository, final SubmittedBatch batch) { - return List.of(new SubmissionReceipt(accepted, batch.issueUrl())); + public SubmissionStatus status(final URI repository, final SubmittedBatch batch) { + return new SubmissionStatus(batch, List.of(new SubmissionReceipt(accepted, batch.issueUrl())), false); } } @@ -137,8 +155,28 @@ public SubmittedBatch createIssue(final URI repository, final String body, final } @Override - public List receipts(final URI repository, final SubmittedBatch batch) { - return List.of(); + public SubmissionStatus status(final URI repository, final SubmittedBatch batch) { + return new SubmissionStatus(batch, List.of(), false); + } + } + + private static final class TerminalRejectionTransport implements IssueOpsTransport { + private final UUID battleId; + private final List bodies = new ArrayList<>(); + + private TerminalRejectionTransport(final UUID battleId) { + this.battleId = battleId; + } + + @Override + public SubmittedBatch createIssue(final URI repository, final String body, final String title) { + bodies.add(body); + return new SubmittedBatch(43, "https://github.com/example/rumble-data/issues/43", List.of(battleId)); + } + + @Override + public SubmissionStatus status(final URI repository, final SubmittedBatch batch) { + return new SubmissionStatus(batch, List.of(), batch.issueNumber() == 42); } } }