Complete folder scheduler verification
This commit is contained in:
parent
8e67566f0d
commit
c08acb5f18
|
|
@ -31,7 +31,7 @@ Note: the local machine's filesystem root is read-only in this workspace, so abs
|
||||||
11. [x] Add unit tests for ordering, invalid-file handling, repeat prevention, and state transitions.
|
11. [x] Add unit tests for ordering, invalid-file handling, repeat prevention, and state transitions.
|
||||||
12. [x] Add optional FFmpeg integration test for a short fixture.
|
12. [x] Add optional FFmpeg integration test for a short fixture.
|
||||||
13. [x] Add local profile or documented runtime config for enabling the scheduler.
|
13. [x] Add local profile or documented runtime config for enabling the scheduler.
|
||||||
14. [ ] Run verification and mark feature complete.
|
14. [x] Run verification and mark feature complete.
|
||||||
|
|
||||||
## Format Recommendation
|
## Format Recommendation
|
||||||
|
|
||||||
|
|
@ -87,8 +87,20 @@ mvn spring-boot:run -Dspring-boot.run.profiles=folder-scheduler-local
|
||||||
|
|
||||||
Place source videos in `input/source`. The service creates `input/working`, `input/processed`, and `input/rejected` at startup and writes clips to `output/clips/<video-name>`.
|
Place source videos in `input/source`. The service creates `input/working`, `input/processed`, and `input/rejected` at startup and writes clips to `output/clips/<video-name>`.
|
||||||
|
|
||||||
|
Producers must copy files with a temporary `.part`, `.tmp`, or `.download` suffix and atomically rename them to the final video filename only after the write completes. The scheduler ignores these temporary suffixes, preventing partially written videos from being rejected by `ffprobe`.
|
||||||
|
|
||||||
For production, keep the default absolute paths and mount persistent writable volumes at `/input` and `/output`. Run only one scheduler replica unless a cross-process lease is added.
|
For production, keep the default absolute paths and mount persistent writable volumes at `/input` and `/output`. Run only one scheduler replica unless a cross-process lease is added.
|
||||||
|
|
||||||
|
## Completion Status
|
||||||
|
|
||||||
|
Completed on 2026-07-10.
|
||||||
|
|
||||||
|
- `mvn -q clean verify`: passed with 57 tests, 0 failures, 0 errors, and 0 skipped tests.
|
||||||
|
- Real FFmpeg integration: a generated 17-second H.264/AAC source produced 3 MP4 clips.
|
||||||
|
- JaCoCo scheduler package coverage: 1,009/1,009 instructions, 52/52 branches, and 205/205 lines covered (100%).
|
||||||
|
- Coverage enforcement and the HTML/XML/CSV report run automatically during Maven `verify`.
|
||||||
|
- Final review fixed conditional Spring component creation and explicit constructor injection; the disabled default profile and enabled scheduler pipeline both have context tests.
|
||||||
|
|
||||||
## Processing Rules
|
## Processing Rules
|
||||||
|
|
||||||
The scheduler should:
|
The scheduler should:
|
||||||
|
|
|
||||||
7
pom.xml
7
pom.xml
|
|
@ -135,6 +135,13 @@
|
||||||
</rules>
|
</rules>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>coverage-report</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>report</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package org.example.videoclips.folder;
|
package org.example.videoclips.folder;
|
||||||
|
|
||||||
import org.example.videoclips.config.VideoClippingProperties;
|
import org.example.videoclips.config.VideoClippingProperties;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -11,11 +13,13 @@ import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ConditionalOnProperty(name = "video-clipping.folder-scheduler.enabled", havingValue = "true")
|
||||||
public class FolderFfmpegClipper {
|
public class FolderFfmpegClipper {
|
||||||
|
|
||||||
private final VideoClippingProperties.FolderScheduler properties;
|
private final VideoClippingProperties.FolderScheduler properties;
|
||||||
private final ProcessExecutor processExecutor;
|
private final ProcessExecutor processExecutor;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public FolderFfmpegClipper(VideoClippingProperties videoClippingProperties) {
|
public FolderFfmpegClipper(VideoClippingProperties videoClippingProperties) {
|
||||||
this(videoClippingProperties, FolderFfmpegClipper::execute);
|
this(videoClippingProperties, FolderFfmpegClipper::execute);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package org.example.videoclips.folder;
|
package org.example.videoclips.folder;
|
||||||
|
|
||||||
import org.example.videoclips.config.VideoClippingProperties;
|
import org.example.videoclips.config.VideoClippingProperties;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -13,6 +15,7 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ConditionalOnProperty(name = "video-clipping.folder-scheduler.enabled", havingValue = "true")
|
||||||
public class FolderVideoValidator {
|
public class FolderVideoValidator {
|
||||||
|
|
||||||
private static final Set<String> ALLOWED_EXTENSIONS = Set.of("mp4", "mov", "m4v", "webm", "mkv");
|
private static final Set<String> ALLOWED_EXTENSIONS = Set.of("mp4", "mov", "m4v", "webm", "mkv");
|
||||||
|
|
@ -20,6 +23,7 @@ public class FolderVideoValidator {
|
||||||
private final VideoClippingProperties.FolderScheduler properties;
|
private final VideoClippingProperties.FolderScheduler properties;
|
||||||
private final ProcessExecutor processExecutor;
|
private final ProcessExecutor processExecutor;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public FolderVideoValidator(VideoClippingProperties videoClippingProperties) {
|
public FolderVideoValidator(VideoClippingProperties videoClippingProperties) {
|
||||||
this(videoClippingProperties, FolderVideoValidator::execute);
|
this(videoClippingProperties, FolderVideoValidator::execute);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.example.videoclips.folder;
|
||||||
|
|
||||||
|
import org.example.videoclips.config.VideoClippingProperties;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class FolderSchedulerSpringContextTest {
|
||||||
|
|
||||||
|
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||||
|
.withBean(VideoClippingProperties.class)
|
||||||
|
.withUserConfiguration(
|
||||||
|
FolderVideoValidator.class,
|
||||||
|
FolderFfmpegClipper.class,
|
||||||
|
FolderVideoScanScheduler.class
|
||||||
|
);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createsCompleteSchedulerPipelineWhenEnabled() {
|
||||||
|
contextRunner
|
||||||
|
.withPropertyValues("video-clipping.folder-scheduler.enabled=true")
|
||||||
|
.run(context -> {
|
||||||
|
assertThat(context).hasSingleBean(FolderVideoValidator.class);
|
||||||
|
assertThat(context).hasSingleBean(FolderFfmpegClipper.class);
|
||||||
|
assertThat(context).hasSingleBean(FolderVideoScanScheduler.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void doesNotCreateSchedulerPipelineWhenDisabled() {
|
||||||
|
contextRunner.run(context -> {
|
||||||
|
assertThat(context).doesNotHaveBean(FolderVideoValidator.class);
|
||||||
|
assertThat(context).doesNotHaveBean(FolderFfmpegClipper.class);
|
||||||
|
assertThat(context).doesNotHaveBean(FolderVideoScanScheduler.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue