mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
test: fix docker test-image env and container-specific test guards
Make the full pnpm test:docker suite pass the env-dependent tests (~85 failures): - Dockerfile.test: ENV LD_LIBRARY_PATH=/usr/local/lib so the built libheif 1.21 is not shadowed by the base image's older system libheif (heif-dec failed with an undefined-symbol error -> 'No HEIF decoder found' on 72 HEIF tests); add libjxl-tools (JXL) and ghostscript + the ImageMagick policy.xml EPS allow-edit. - docker-compose.test.yml: SYNC_WAIT_MS=30000 so sync-wait image tools do not fall back to 202 under single-container contention (10 tests). - install_feature.py: guard tarfile.extractall(filter='data') behind Python>=3.12 (bookworm ships 3.11); the manual entry guards already protect. - feature-status.test.ts / docker-file-secrets.test.ts: skip the two cases that cannot hold inside the container (/.dockerenv always present; root bypasses chmod). Verified on host: all still pass.
This commit is contained in:
@@ -39,6 +39,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libimage-exiftool-perl \
|
||||
imagemagick \
|
||||
libraw-dev \
|
||||
libjxl-tools \
|
||||
ghostscript \
|
||||
&& if apt-cache show libx265-199 >/dev/null 2>&1; then \
|
||||
apt-get install -y --no-install-recommends libx265-199; \
|
||||
elif apt-cache show libx265-209 >/dev/null 2>&1; then \
|
||||
@@ -48,8 +50,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
|
||||
COPY --from=libheif-builder /opt/libheif/bin/ /usr/local/bin/
|
||||
COPY --from=libheif-builder /opt/libheif/lib/ /usr/local/lib/
|
||||
# The base image ships an older system libheif (~1.15) that shadows our built
|
||||
# 1.21 without this, so heif-dec fails with an undefined-symbol error.
|
||||
ENV LD_LIBRARY_PATH=/usr/local/lib
|
||||
RUN ldconfig
|
||||
|
||||
# Allow ImageMagick's Ghostscript delegate to read/write EPS (the default
|
||||
# Debian policy.xml blocks it).
|
||||
RUN POLICY_FILE=$(find /etc/ImageMagick* -name policy.xml 2>/dev/null | head -1) && \
|
||||
if [ -n "$POLICY_FILE" ]; then \
|
||||
sed -i 's/<policy domain="coder" rights="none" pattern="EPS"/<policy domain="coder" rights="read|write" pattern="EPS"/' "$POLICY_FILE"; \
|
||||
fi
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy workspace config first (for layer caching)
|
||||
|
||||
@@ -25,6 +25,9 @@ services:
|
||||
- WORKSPACE_PATH=/tmp/test-workspace
|
||||
- MAX_MEGAPIXELS=100
|
||||
- RATE_LIMIT_PER_MIN=1000
|
||||
# Single constrained container; give sync-wait tools more headroom before
|
||||
# the factory falls back to async 202 (default is 8s).
|
||||
- SYNC_WAIT_MS=30000
|
||||
tmpfs:
|
||||
- /tmp/test-workspace
|
||||
- /tmp
|
||||
|
||||
@@ -203,7 +203,12 @@ def safe_extract(tar_path: str, staging_dir: str) -> None:
|
||||
# Block absolute paths and traversal
|
||||
if member.name.startswith("/") or ".." in member.name.split("/"):
|
||||
raise RuntimeError(f"Blocked unsafe tar path: {member.name}")
|
||||
tf.extractall(staging_dir, filter="data")
|
||||
# The filter= kwarg was added in Python 3.12; the manual guards above
|
||||
# already block unsafe entries on older interpreters (e.g. 3.11).
|
||||
if sys.version_info >= (3, 12):
|
||||
tf.extractall(staging_dir, filter="data")
|
||||
else:
|
||||
tf.extractall(staging_dir)
|
||||
|
||||
|
||||
# -- File move --
|
||||
|
||||
@@ -117,7 +117,9 @@ describe("Docker _FILE secret convention", () => {
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it("errors when _FILE points to unreadable file", () => {
|
||||
// root (the test container's uid) bypasses chmod-based permission checks, so
|
||||
// the unreadable-file case cannot be exercised there; skip it.
|
||||
it.skipIf(process.getuid?.() === 0)("errors when _FILE points to unreadable file", () => {
|
||||
const secretPath = writeSecret("unreadable.txt", "secret");
|
||||
chmodSync(secretPath, 0o000);
|
||||
try {
|
||||
|
||||
@@ -669,13 +669,18 @@ describe("ensureAiDirs", () => {
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("is a no-op outside managed environments (no manifest, no /.dockerenv)", async () => {
|
||||
process.env.FEATURE_MANIFEST_PATH = join(tempDir, "missing-manifest.json");
|
||||
process.env.DATA_DIR = join(tempDir, "fresh-data");
|
||||
vi.resetModules();
|
||||
mod = await import("../../../apps/api/src/lib/feature-status.js");
|
||||
// /.dockerenv always exists inside the test container, which makes
|
||||
// isDockerEnvironment() true regardless of the manifest path; skip there.
|
||||
it.skipIf(existsSync("/.dockerenv"))(
|
||||
"is a no-op outside managed environments (no manifest, no /.dockerenv)",
|
||||
async () => {
|
||||
process.env.FEATURE_MANIFEST_PATH = join(tempDir, "missing-manifest.json");
|
||||
process.env.DATA_DIR = join(tempDir, "fresh-data");
|
||||
vi.resetModules();
|
||||
mod = await import("../../../apps/api/src/lib/feature-status.js");
|
||||
|
||||
mod.ensureAiDirs();
|
||||
expect(existsSync(join(tempDir, "fresh-data"))).toBe(false);
|
||||
});
|
||||
mod.ensureAiDirs();
|
||||
expect(existsSync(join(tempDir, "fresh-data"))).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user