test: prove 2.8 to 2.9.0 upgrade is lossless and fix what it caught

This commit is contained in:
LukeGus committed 2026-09-25 09:58:45 -05:00
1 parent 2ee9b3592c
commit f779d6a08e
37 files changed
+5184 -377

No files matched your search

@@ -0,0 +1,39 @@
import fs from "node:fs/promises";
import path from "node:path";
export function isInside(file: string, dir: string): boolean {
return path.resolve(file).startsWith(`${path.resolve(dir)}${path.sep}`);
}
/**
* Moves a finished recording someone else wrote (guacd's, in remote
* desktop's recordings folder) under this plugin's data folder, which is the
* only place playback and retention read from. Returns where it ended up;
* a file that is not there is left alone.
*/
export async function adoptRecordingFile(
source: string,
dataDir: string,
): Promise<string> {
if (isInside(source, dataDir)) return source;
const target = path.join(
dataDir,
"session_recordings",
path.basename(path.dirname(source)),
path.basename(source),
);
try {
await fs.access(source);
} catch {
return source;
}
await fs.mkdir(path.dirname(target), { recursive: true });
try {
await fs.rename(source, target);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "EXDEV") throw error;
await fs.copyFile(source, target);
await fs.unlink(source);
}
return target;
}
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import type { PluginContext } from "@termix/plugin-sdk/backend";
import type { SessionRecordingRepository } from "./repository.js";
import { adoptRecordingFile } from "./files.js";
/**
* recordings.writer v1, the service ssh-terminal calls to record sessions.
@@ -40,8 +41,8 @@ export interface RecordingsWriterV1 {
/**
* Inserts a row for a recording a caller already wrote to disk itself
* (remote desktop's guacd recordings), rather than streaming through
* open()/append(). The caller already decided to record; this only writes
* the row.
* open()/append(). The caller already decided to record. The file is
* moved under this plugin's data folder, where playback reads it.
*/
createFinished: (input: {
hostId: number;
@@ -117,7 +118,15 @@ export function createRecordingsWriter(
async createFinished(input) {
const username = await repository.usernameFor(input.userId);
const row = await repository.create({ ...input, username });
const recordingPath = await adoptRecordingFile(
input.recordingPath,
await ctx.files.dataDir(),
).catch(() => input.recordingPath);
const row = await repository.create({
...input,
recordingPath,
username,
});
return { id: row.id };
},
};