humainflow-web/src/app/layouts/tasks-executor/tasks-executor.spec.ts

211 lines
6.1 KiB
TypeScript

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { signal } from '@angular/core';
import { ActivatedRoute, convertToParamMap, provideRouter, Router } from '@angular/router';
import { BlocksService } from '@services/blocks/blocks';
import { ContainersService } from '@services/containers/containers';
import { HumanInteractionDialogService } from '@services/dialogs/human-interaction-dialog';
import { ConfirmDialogService } from '@services/dialogs/confirm-dialog';
import { NodeSettingsDialogService } from '@services/dialogs/node-settings-dialog';
import { FieldRetriever } from '@services/retriever/field-retriever';
import { TaskExecutionsService } from '@services/task-executions/task-executions';
import { normalizeExecutionStatus, TaskExecution } from '@models/task-execution';
import { of } from 'rxjs';
import { vi } from 'vitest';
import { findInteractiveSubflowTargets, TasksExecutor } from './tasks-executor';
describe('TasksExecutor', () => {
let component: TasksExecutor;
let fixture: ComponentFixture<TasksExecutor>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TasksExecutor],
providers: [
provideRouter([]),
{
provide: ActivatedRoute,
useValue: {
queryParamMap: of(convertToParamMap({})),
snapshot: {
queryParamMap: convertToParamMap({})
}
}
},
{
provide: Router,
useValue: {
navigate: vi.fn().mockResolvedValue(true)
}
},
{
provide: TaskExecutionsService,
useValue: {
taskExecutions: signal([]),
taskExecutionGroups: signal([]),
followedExecutions: signal({}),
pendingExecutionCreation: signal(false),
init: vi.fn(),
retrieveExecution: vi.fn().mockReturnValue(of(null)),
deleteExecution: vi.fn().mockReturnValue(of(null)),
rerunExecution: vi.fn().mockReturnValue(of(null))
}
},
{
provide: ConfirmDialogService,
useValue: {
open: vi.fn().mockResolvedValue(true)
}
},
{
provide: BlocksService,
useValue: {
getAllBlocksTypes: vi.fn().mockResolvedValue(signal([]))
}
},
{
provide: ContainersService,
useValue: {
getAllContainerTypes: vi.fn().mockResolvedValue(signal([]))
}
},
{
provide: HumanInteractionDialogService,
useValue: {
state: signal(null),
close: vi.fn(),
update: vi.fn()
}
},
{
provide: NodeSettingsDialogService,
useValue: {
open: vi.fn().mockResolvedValue(null)
}
},
{
provide: FieldRetriever,
useValue: {
retrieveText: vi.fn(),
retrieveSchema: vi.fn(),
retrieveStructuredData: vi.fn()
}
}
]
})
.compileComponents();
fixture = TestBed.createComponent(TasksExecutor);
component = fixture.componentInstance;
fixture.detectChanges();
await fixture.whenStable();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('toggles the execution tree panel', () => {
expect(component.executionTreeOpen()).toBe(true);
component.toggleExecutionTree();
expect(component.executionTreeOpen()).toBe(false);
component.toggleExecutionTree();
expect(component.executionTreeOpen()).toBe(true);
});
it('follows only container steps waiting for a child subflow', () => {
const execution = {
id: 'parent-1',
name: 'Parent',
creationTime: 1,
context: {
inputs: {},
result: {},
errors: {},
warnings: [],
status: 'WAITING',
waitingSteps: ['container-1'],
steps: {
'container-1': {
id: 'container-1',
status: 'WAITING_FOR_SUBFLOW',
simulated: false,
activeInnerExecutionId: 'child-2',
containerContinuationPhase: 'WAITING_FOR_SUBFLOW',
containerIterationIndex: 2,
node: {
id: 'container-1',
name: 'Review loop',
inputs: [],
outputs: [],
typeName: 'LoopContainer',
nodeFamily: 'container',
specificConfiguration: {}
}
},
'human-1': {
id: 'human-1',
status: 'WAITING_FOR_INTERACTION',
simulated: false
}
}
}
} satisfies TaskExecution;
expect(findInteractiveSubflowTargets(execution)).toEqual([
expect.objectContaining({
childExecutionId: 'child-2',
parentStepId: 'container-1',
containerName: 'Review loop',
iterationIndex: 2
})
]);
});
it('detects a new child id as the next container iteration', () => {
const step = {
id: 'iterator-1',
status: 'WAITING_FOR_SUBFLOW',
simulated: false,
activeInnerExecutionId: 'child-1',
containerIterationIndex: 1
};
const execution = {
id: 'parent-1',
name: 'Parent',
creationTime: 1,
context: {
inputs: {},
result: {},
errors: {},
warnings: [],
status: 'WAITING',
waitingSteps: ['iterator-1'],
steps: { 'iterator-1': step }
}
} satisfies TaskExecution;
expect(findInteractiveSubflowTargets(execution)[0]?.childExecutionId).toBe('child-1');
const nextExecution = {
...execution,
context: {
...execution.context,
steps: {
'iterator-1': {
...step,
activeInnerExecutionId: 'child-2',
containerIterationIndex: 2
}
}
}
} satisfies TaskExecution;
expect(findInteractiveSubflowTargets(nextExecution)[0]).toEqual(expect.objectContaining({
childExecutionId: 'child-2',
iterationIndex: 2
}));
expect(normalizeExecutionStatus('WAITING_FOR_SUBFLOW')).toBe('WAITING');
});
});