@straiffi/archon 1.0.13 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/TestsDialog-CXwEw4cO.js +5 -0
- package/dist/client/assets/badge-DBRMkzQk.js +41 -0
- package/dist/client/assets/index-BRYAyBgT.js +142 -0
- package/dist/client/assets/index-CSG_tXky.css +2 -0
- package/dist/client/assets/rolldown-runtime-BYbx6iT9.js +1 -0
- package/dist/client/index.html +4 -2
- package/dist/server/db.js +2 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +259 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent.js +30 -1
- package/dist/server/lib/agent.js.map +1 -1
- package/dist/server/lib/projectAutoConfig.js +98 -4
- package/dist/server/lib/projectAutoConfig.js.map +1 -1
- package/dist/server/lib/projects.js +78 -0
- package/dist/server/lib/projects.js.map +1 -1
- package/dist/server/lib/testCommandRunner.js +155 -0
- package/dist/server/lib/testCommandRunner.js.map +1 -0
- package/dist/server/lib/testSessions.js +494 -0
- package/dist/server/lib/testSessions.js.map +1 -0
- package/package.json +1 -1
- package/dist/client/assets/index-BhQzJS4j.css +0 -2
- package/dist/client/assets/index-D3G9pPr9.js +0 -177
package/dist/server/index.js
CHANGED
|
@@ -40,6 +40,7 @@ import { JiraPlanningReferenceError, resolveJiraPlanningDescription } from './li
|
|
|
40
40
|
import { getBundlePullRequestRecord, hasOpenBundlePullRequest, serializeBundlePullRequest, shouldSyncBundlePullRequest, upsertBundlePullRequest, } from './lib/bundlePullRequests.js';
|
|
41
41
|
import { countInboundProjectLinks, getEffectiveProject, getProjectById, getProjectTicketStats, hasProjectRunIde, listProjects, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectPayload, validateRepoPath, } from './lib/projects.js';
|
|
42
42
|
import { getActiveRunStatus, getActiveRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runProject, runSetupCommandsInWorkspace, runTicketInWorkspace, stopAllTickets, stopProjectRun, stopTicket } from './lib/run.js';
|
|
43
|
+
import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession, findProjectTestCommand, getTestSession, isTestSelectionSupported, startTestSessionRun, stopTestSessionRun } from './lib/testSessions.js';
|
|
43
44
|
import { getActiveBundleStageChangeHead, undoTicketStage } from './lib/ticketUndo.js';
|
|
44
45
|
import { closeAllTerminalSessions, createProjectTerminalSession, createTerminalSessionForWorkspace, destroyTerminalSessionById, registerTerminalSocketHandlers, } from './lib/terminal.js';
|
|
45
46
|
import { countBundleTickets, createTicketRecord, createBundle, deleteBundle, ensureProjectRootBundle, autoParkTicketIfStale, clearAutoParkDismissalIfNeeded, assignTicketDoneOrder, assignTicketLaneOrder, getBundle, getBundleByName, getBundleByBranch, getBundleRepresentativeTicketContext, isProjectRootBundle, getTicket, isBundledTicket, listTicketsInRunContext, listBundles, listBoardTicketEnrichment, listTickets, resolveTicketBranch, resolveTicketTool, } from './lib/tickets.js';
|
|
@@ -697,6 +698,121 @@ const resolveProjectTargetGitWorkspace = (project) => {
|
|
|
697
698
|
}),
|
|
698
699
|
};
|
|
699
700
|
};
|
|
701
|
+
const stripRepeatedSelfSuffix = (label) => {
|
|
702
|
+
let nextLabel = label.trim();
|
|
703
|
+
while (nextLabel !== '') {
|
|
704
|
+
const match = nextLabel.match(/^(.*?) \(([^()]+)\)$/);
|
|
705
|
+
if (!match) {
|
|
706
|
+
return nextLabel;
|
|
707
|
+
}
|
|
708
|
+
const prefix = match[1]?.trim() ?? '';
|
|
709
|
+
const suffix = match[2]?.trim() ?? '';
|
|
710
|
+
const normalizedPrefix = prefix.toLowerCase();
|
|
711
|
+
const normalizedSuffix = suffix.toLowerCase();
|
|
712
|
+
if (normalizedPrefix === normalizedSuffix) {
|
|
713
|
+
nextLabel = prefix;
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
if (normalizedPrefix.endsWith(` (${normalizedSuffix})`)) {
|
|
717
|
+
nextLabel = prefix;
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
return nextLabel;
|
|
721
|
+
}
|
|
722
|
+
return label.trim();
|
|
723
|
+
};
|
|
724
|
+
const stripRepeatedBranchSuffix = (label, branch) => {
|
|
725
|
+
const trimmedLabel = stripRepeatedSelfSuffix(label);
|
|
726
|
+
const trimmedBranch = branch.trim();
|
|
727
|
+
if (trimmedLabel === '' || trimmedBranch === '') {
|
|
728
|
+
return trimmedLabel;
|
|
729
|
+
}
|
|
730
|
+
const suffix = ` (${trimmedBranch})`;
|
|
731
|
+
let nextLabel = trimmedLabel;
|
|
732
|
+
while (nextLabel.toLowerCase().endsWith(suffix.toLowerCase())) {
|
|
733
|
+
nextLabel = nextLabel.slice(0, -suffix.length).trimEnd();
|
|
734
|
+
}
|
|
735
|
+
return nextLabel || trimmedLabel;
|
|
736
|
+
};
|
|
737
|
+
const getBundleTestTargetLabel = (bundle) => {
|
|
738
|
+
const displayName = stripRepeatedSelfSuffix(bundle.name || bundle.branch || '');
|
|
739
|
+
return bundle.branch ? stripRepeatedBranchSuffix(displayName, bundle.branch) : displayName;
|
|
740
|
+
};
|
|
741
|
+
const resolveExplicitProjectTestWorkspace = (project, targetKind, targetBundleId) => {
|
|
742
|
+
if (targetKind === 'bundle') {
|
|
743
|
+
if (!targetBundleId) {
|
|
744
|
+
return { error: 'target_bundle_id is required for bundle test runs', status: 400 };
|
|
745
|
+
}
|
|
746
|
+
const bundle = getBundle(targetBundleId, project.id);
|
|
747
|
+
if (!bundle || isProjectRootBundle(bundle)) {
|
|
748
|
+
return { error: 'Selected bundle target no longer exists', status: 409 };
|
|
749
|
+
}
|
|
750
|
+
const cwd = resolveExistingWorktreePath(bundle.branch, project);
|
|
751
|
+
if (!cwd) {
|
|
752
|
+
return { error: 'Selected bundle target does not have a prepared worktree', status: 409 };
|
|
753
|
+
}
|
|
754
|
+
return {
|
|
755
|
+
project,
|
|
756
|
+
cwd,
|
|
757
|
+
target_kind: 'bundle',
|
|
758
|
+
target_bundle_id: bundle.id,
|
|
759
|
+
target_label: getBundleTestTargetLabel(bundle),
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
const workspace = resolveProjectRootGitWorkspace(project);
|
|
763
|
+
if ('error' in workspace) {
|
|
764
|
+
return workspace;
|
|
765
|
+
}
|
|
766
|
+
return {
|
|
767
|
+
project,
|
|
768
|
+
cwd: workspace.cwd,
|
|
769
|
+
target_kind: 'repo_root',
|
|
770
|
+
target_bundle_id: null,
|
|
771
|
+
target_label: `Repo root (${workspace.branch})`,
|
|
772
|
+
};
|
|
773
|
+
};
|
|
774
|
+
const resolveTicketTestWorkspace = (ticket) => {
|
|
775
|
+
const workspace = resolveTicketGitWorkspace(ticket);
|
|
776
|
+
if ('error' in workspace) {
|
|
777
|
+
return workspace;
|
|
778
|
+
}
|
|
779
|
+
const project = workspace.project;
|
|
780
|
+
if (ticket.worktree_bundle_id) {
|
|
781
|
+
const bundle = getBundle(ticket.worktree_bundle_id, project.id);
|
|
782
|
+
if (bundle && !isProjectRootBundle(bundle)) {
|
|
783
|
+
return {
|
|
784
|
+
project,
|
|
785
|
+
cwd: workspace.cwd,
|
|
786
|
+
target_kind: 'bundle',
|
|
787
|
+
target_bundle_id: bundle.id,
|
|
788
|
+
target_label: getBundleTestTargetLabel(bundle),
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
project,
|
|
794
|
+
cwd: workspace.cwd,
|
|
795
|
+
target_kind: 'repo_root',
|
|
796
|
+
target_bundle_id: null,
|
|
797
|
+
target_label: `Repo root (${workspace.branch})`,
|
|
798
|
+
};
|
|
799
|
+
};
|
|
800
|
+
const parseTestTargetSelection = (value) => {
|
|
801
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || !hasOwn(value, 'kind')) {
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
const selection = value;
|
|
805
|
+
if (selection.kind === 'all') {
|
|
806
|
+
return { kind: 'all' };
|
|
807
|
+
}
|
|
808
|
+
if (selection.kind === 'file' && typeof selection.path === 'string') {
|
|
809
|
+
return { kind: 'file', path: selection.path };
|
|
810
|
+
}
|
|
811
|
+
if (selection.kind === 'case' && typeof selection.path === 'string' && typeof selection.name === 'string') {
|
|
812
|
+
return { kind: 'case', path: selection.path, name: selection.name };
|
|
813
|
+
}
|
|
814
|
+
return null;
|
|
815
|
+
};
|
|
700
816
|
const resolveProjectFileSuggestionWorkspace = (project, options = {}) => {
|
|
701
817
|
const ticketId = options.ticketId?.trim() || null;
|
|
702
818
|
if (ticketId) {
|
|
@@ -3224,7 +3340,7 @@ app.post('/projects', (req, res) => {
|
|
|
3224
3340
|
return res.status(400).json({ error: validation.error });
|
|
3225
3341
|
}
|
|
3226
3342
|
const projectCount = Number(db.prepare('SELECT COUNT(*) AS count FROM projects').get()?.count ?? 0);
|
|
3227
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, worktree_sync, } = validation.values;
|
|
3343
|
+
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, worktree_sync, } = validation.values;
|
|
3228
3344
|
const id = randomUUID();
|
|
3229
3345
|
db.prepare(`
|
|
3230
3346
|
INSERT INTO projects (
|
|
@@ -3233,6 +3349,7 @@ app.post('/projects', (req, res) => {
|
|
|
3233
3349
|
repo_path,
|
|
3234
3350
|
run_setup,
|
|
3235
3351
|
run_services,
|
|
3352
|
+
test_commands,
|
|
3236
3353
|
run_ide,
|
|
3237
3354
|
preview_service_name,
|
|
3238
3355
|
preview_path,
|
|
@@ -3244,8 +3361,8 @@ app.post('/projects', (req, res) => {
|
|
|
3244
3361
|
memory_enabled,
|
|
3245
3362
|
worktree_sync
|
|
3246
3363
|
)
|
|
3247
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3248
|
-
`).run(id, name, repo_path, run_setup, run_services, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, commit_message_rules ?? null, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, worktree_sync ?? null);
|
|
3364
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3365
|
+
`).run(id, name, repo_path, run_setup, run_services, test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, commit_message_rules ?? null, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, worktree_sync ?? null);
|
|
3249
3366
|
replaceProjectLinks(id, linked_project_ids ?? []);
|
|
3250
3367
|
ensureProjectRootBundle(id);
|
|
3251
3368
|
if (projectCount === 0) {
|
|
@@ -4122,13 +4239,14 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
4122
4239
|
});
|
|
4123
4240
|
}
|
|
4124
4241
|
}
|
|
4125
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, worktree_sync, } = validation.values;
|
|
4242
|
+
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, worktree_sync, } = validation.values;
|
|
4126
4243
|
db.prepare(`
|
|
4127
4244
|
UPDATE projects SET
|
|
4128
4245
|
name = ?,
|
|
4129
4246
|
repo_path = ?,
|
|
4130
4247
|
run_setup = ?,
|
|
4131
4248
|
run_services = ?,
|
|
4249
|
+
test_commands = ?,
|
|
4132
4250
|
run_ide = ?,
|
|
4133
4251
|
preview_service_name = ?,
|
|
4134
4252
|
preview_path = ?,
|
|
@@ -4141,7 +4259,7 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
4141
4259
|
worktree_sync = ?,
|
|
4142
4260
|
updated_at = CURRENT_TIMESTAMP
|
|
4143
4261
|
WHERE id = ?
|
|
4144
|
-
`).run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, req.params.id);
|
|
4262
|
+
`).run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, req.params.id);
|
|
4145
4263
|
if (hasOwn(validation.values, 'linked_project_ids')) {
|
|
4146
4264
|
replaceProjectLinks(project.id, linked_project_ids ?? []);
|
|
4147
4265
|
}
|
|
@@ -4902,6 +5020,141 @@ app.post('/projects/:id/run', (req, res) => {
|
|
|
4902
5020
|
targetBundleId: workspace.target_bundle_id,
|
|
4903
5021
|
});
|
|
4904
5022
|
});
|
|
5023
|
+
app.post('/projects/:id/test-sessions', (req, res) => {
|
|
5024
|
+
const project = getProjectById(req.params.id);
|
|
5025
|
+
if (!project) {
|
|
5026
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5027
|
+
}
|
|
5028
|
+
const body = req.body && typeof req.body === 'object' && !Array.isArray(req.body) ? req.body : null;
|
|
5029
|
+
const targetKind = body?.target_kind === 'bundle'
|
|
5030
|
+
? 'bundle'
|
|
5031
|
+
: body?.target_kind === 'repo_root'
|
|
5032
|
+
? 'repo_root'
|
|
5033
|
+
: null;
|
|
5034
|
+
if (!targetKind) {
|
|
5035
|
+
return res.status(400).json({ error: 'target_kind must be repo_root or bundle' });
|
|
5036
|
+
}
|
|
5037
|
+
const targetBundleId = typeof body?.target_bundle_id === 'string' ? body.target_bundle_id : null;
|
|
5038
|
+
const workspace = resolveExplicitProjectTestWorkspace(project, targetKind, targetBundleId);
|
|
5039
|
+
if ('error' in workspace) {
|
|
5040
|
+
return res.status(Number(workspace.status)).json({ error: workspace.error });
|
|
5041
|
+
}
|
|
5042
|
+
return res.status(201).json(createTestSession({
|
|
5043
|
+
projectId: project.id,
|
|
5044
|
+
targetKind: workspace.target_kind,
|
|
5045
|
+
targetBundleId: workspace.target_bundle_id,
|
|
5046
|
+
cwd: workspace.cwd,
|
|
5047
|
+
targetLabel: workspace.target_label,
|
|
5048
|
+
}));
|
|
5049
|
+
});
|
|
5050
|
+
app.post('/tickets/:id/test-sessions', (req, res) => {
|
|
5051
|
+
const ticket = getTicket(req.params.id);
|
|
5052
|
+
if (!ticket) {
|
|
5053
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5054
|
+
}
|
|
5055
|
+
if (!matchesTicketProjectContext(ticket, req)) {
|
|
5056
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5057
|
+
}
|
|
5058
|
+
const workspace = resolveTicketTestWorkspace(ticket);
|
|
5059
|
+
if ('error' in workspace) {
|
|
5060
|
+
return res.status(workspace.status).json({ error: workspace.error });
|
|
5061
|
+
}
|
|
5062
|
+
return res.status(201).json(createTestSession({
|
|
5063
|
+
projectId: workspace.project.id,
|
|
5064
|
+
ticketId: ticket.id,
|
|
5065
|
+
targetKind: workspace.target_kind,
|
|
5066
|
+
targetBundleId: workspace.target_bundle_id,
|
|
5067
|
+
cwd: workspace.cwd,
|
|
5068
|
+
targetLabel: workspace.target_label,
|
|
5069
|
+
}));
|
|
5070
|
+
});
|
|
5071
|
+
app.get('/test-sessions/:id/discovery', (req, res) => {
|
|
5072
|
+
const session = getTestSession(req.params.id);
|
|
5073
|
+
if (!session) {
|
|
5074
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5075
|
+
}
|
|
5076
|
+
const commandId = typeof req.query.command_id === 'string' ? req.query.command_id.trim() : '';
|
|
5077
|
+
let command = null;
|
|
5078
|
+
if (commandId !== '') {
|
|
5079
|
+
const project = getProjectById(session.project_id);
|
|
5080
|
+
if (!project) {
|
|
5081
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
5082
|
+
}
|
|
5083
|
+
command = findProjectTestCommand(project, commandId);
|
|
5084
|
+
if (!command) {
|
|
5085
|
+
return res.status(404).json({ error: 'Test command not found' });
|
|
5086
|
+
}
|
|
5087
|
+
}
|
|
5088
|
+
try {
|
|
5089
|
+
const supportsGranularSelection = command
|
|
5090
|
+
? isTestSelectionSupported(command, { kind: 'file', path: '' }, session.cwd)
|
|
5091
|
+
: false;
|
|
5092
|
+
return res.json({
|
|
5093
|
+
files: discoverTestsForSession(session.id, command),
|
|
5094
|
+
supports_granular_selection: supportsGranularSelection,
|
|
5095
|
+
});
|
|
5096
|
+
}
|
|
5097
|
+
catch (error) {
|
|
5098
|
+
return res.status(500).json({ error: getErrorMessage(error) });
|
|
5099
|
+
}
|
|
5100
|
+
});
|
|
5101
|
+
app.post('/test-sessions/:id/run', async (req, res) => {
|
|
5102
|
+
const session = getTestSession(req.params.id);
|
|
5103
|
+
if (!session) {
|
|
5104
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5105
|
+
}
|
|
5106
|
+
const project = getProjectById(session.project_id);
|
|
5107
|
+
if (!project) {
|
|
5108
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
5109
|
+
}
|
|
5110
|
+
const body = req.body && typeof req.body === 'object' && !Array.isArray(req.body) ? req.body : null;
|
|
5111
|
+
const commandId = typeof body?.command_id === 'string' ? body.command_id.trim() : '';
|
|
5112
|
+
if (commandId === '') {
|
|
5113
|
+
return res.status(400).json({ error: 'command_id is required' });
|
|
5114
|
+
}
|
|
5115
|
+
const selection = parseTestTargetSelection(body?.selection);
|
|
5116
|
+
if (!selection) {
|
|
5117
|
+
return res.status(400).json({ error: 'selection must be all, file, or case' });
|
|
5118
|
+
}
|
|
5119
|
+
const command = findProjectTestCommand(project, commandId);
|
|
5120
|
+
if (!command) {
|
|
5121
|
+
return res.status(404).json({ error: 'Test command not found' });
|
|
5122
|
+
}
|
|
5123
|
+
if (!isTestSelectionSupported(command, selection, session.cwd)) {
|
|
5124
|
+
return res.status(409).json({ error: 'The selected test command only supports running the full suite.' });
|
|
5125
|
+
}
|
|
5126
|
+
if (selection.kind !== 'all' && !doesTestFileExistForSession(session.id, selection.path, command)) {
|
|
5127
|
+
return res.status(404).json({ error: 'Selected test file no longer exists in this target workspace.' });
|
|
5128
|
+
}
|
|
5129
|
+
try {
|
|
5130
|
+
return res.json(await startTestSessionRun(session.id, project, {
|
|
5131
|
+
command,
|
|
5132
|
+
selection,
|
|
5133
|
+
}, io));
|
|
5134
|
+
}
|
|
5135
|
+
catch (error) {
|
|
5136
|
+
return res.status(409).json({ error: getErrorMessage(error) });
|
|
5137
|
+
}
|
|
5138
|
+
});
|
|
5139
|
+
app.post('/test-sessions/:id/stop', async (req, res) => {
|
|
5140
|
+
const session = getTestSession(req.params.id);
|
|
5141
|
+
if (!session) {
|
|
5142
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5143
|
+
}
|
|
5144
|
+
try {
|
|
5145
|
+
return res.json(await stopTestSessionRun(session.id, io));
|
|
5146
|
+
}
|
|
5147
|
+
catch (error) {
|
|
5148
|
+
return res.status(500).json({ error: getErrorMessage(error) });
|
|
5149
|
+
}
|
|
5150
|
+
});
|
|
5151
|
+
app.delete('/test-sessions/:id', async (req, res) => {
|
|
5152
|
+
const deleted = await deleteTestSession(req.params.id, io);
|
|
5153
|
+
if (!deleted) {
|
|
5154
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5155
|
+
}
|
|
5156
|
+
return res.status(204).send();
|
|
5157
|
+
});
|
|
4905
5158
|
app.post('/tickets/:id/terminal', (req, res) => {
|
|
4906
5159
|
const ticket = getTicket(req.params.id);
|
|
4907
5160
|
if (!ticket) {
|
|
@@ -5219,6 +5472,7 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
|
|
|
5219
5472
|
});
|
|
5220
5473
|
};
|
|
5221
5474
|
export const shutdownServer = async (signal) => {
|
|
5475
|
+
await clearAllTestSessions(io);
|
|
5222
5476
|
await shutdownRealtimeServer({
|
|
5223
5477
|
signal,
|
|
5224
5478
|
io,
|