diff --git a/src/test/java/com/ddf/vodsystem/services/media/StreamActionsServiceTest.java b/src/test/java/com/ddf/vodsystem/services/media/StreamActionsServiceTest.java index cb0d563..0d9e02c 100644 --- a/src/test/java/com/ddf/vodsystem/services/media/StreamActionsServiceTest.java +++ b/src/test/java/com/ddf/vodsystem/services/media/StreamActionsServiceTest.java @@ -45,6 +45,23 @@ class StreamActionsServiceTest { Files.createFile(tempDir.resolve(name)); } + /** + * Stubs the two-arg {@link CommandRunner#run} so the head re-encode pass + * actually writes a non-empty file at its output path — {@code encodeHead} + * verifies its output exists and is non-empty before continuing. The concat + * pass (no {@code mpegts} muxer) is left untouched. Returns {@code output} + * for both invocations. + */ + private void stubRunnerProducingHead(CommandOutput output) throws IOException, InterruptedException { + when(commandRunner.run(anyList(), any())).thenAnswer(inv -> { + List cmd = inv.getArgument(0); + if (cmd.contains("mpegts")) { + Files.writeString(Path.of(cmd.get(cmd.size() - 1)), "x"); + } + return output; + }); + } + // --------------------------------------------------------------- // getSegmentsInRange: missing / invalid directories // --------------------------------------------------------------- @@ -279,23 +296,25 @@ class StreamActionsServiceTest { ProgressTracker progress = new ProgressTracker(); CommandOutput commandOutput = new CommandOutput(); - when(commandRunner.run(anyList())).thenReturn(commandOutput); - when(commandRunner.run(anyList(), any())).thenReturn(commandOutput); + stubRunnerProducingHead(commandOutput); var future = service.saveSection(List.of(segmentA, segmentB), 5.0f, 12.0f, output, progress); assertThat(future.get()).isSameAs(commandOutput); assertThat(progress.isComplete()).isTrue(); + // saveSection issues two ffmpeg passes: the head re-encode (trims the + // first segment via -ss) followed by the stream-copy concat (-t/-c copy). @SuppressWarnings("unchecked") ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - Mockito.verify(commandRunner).run(captor.capture(), any()); - List command = captor.getValue(); + Mockito.verify(commandRunner, Mockito.times(2)).run(captor.capture(), any()); + List headCommand = captor.getAllValues().get(0); + List concatCommand = captor.getAllValues().get(1); - assertThat(command).containsSequence("-ss", "5.0"); - assertThat(command).containsSequence("-t", "12.0"); - assertThat(command).containsSequence("-c", "copy"); - assertThat(command).endsWith(output.toAbsolutePath().toString()); + assertThat(headCommand).containsSequence("-ss", "5.0"); + assertThat(concatCommand).containsSequence("-t", "12.0"); + assertThat(concatCommand).containsSequence("-c", "copy"); + assertThat(concatCommand).endsWith(output.toAbsolutePath().toString()); } @Test @@ -303,7 +322,7 @@ class StreamActionsServiceTest { Path output = Path.of("/tmp/out/clip.mp4"); ProgressTracker progress = new ProgressTracker(); - when(commandRunner.run(anyList())).thenReturn(new CommandOutput()); + // The head re-encode is the first ffmpeg pass; its failure must surface. when(commandRunner.run(anyList(), any())).thenThrow(new IOException("ffmpeg boom")); var future = service.saveSection(List.of(Path.of("/tmp/a.ts")), 0f, 10f, output, progress); @@ -328,9 +347,12 @@ class StreamActionsServiceTest { } @Test - void saveSection_nullOutputFile_completesExceptionally() throws IOException { + void saveSection_nullOutputFile_completesExceptionally() throws Exception { ProgressTracker progress = new ProgressTracker(); + // Let the head pass succeed so execution reaches the null output file. + stubRunnerProducingHead(new CommandOutput()); + var future = service.saveSection(List.of(Path.of("/tmp/a.ts")), 0f, 10f, null, progress); assertThatThrownBy(future::get)