fix(mosaic): preserve v1 agent name semantics
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
This commit is contained in:
@@ -387,6 +387,32 @@ describe('v1-to-v2 migration preview', (): void => {
|
|||||||
expect(preview.canonicalRoster?.agents.map(({ name }) => name)).toEqual(['coder0']);
|
expect(preview.canonicalRoster?.agents.map(({ name }) => name)).toEqual(['coder0']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('matches production v1 rejection for a whitespace-padded agent name', async () => {
|
||||||
|
const source = v1Source.replace('name: coder0', 'name: " coder0 "');
|
||||||
|
const rosterPath = join(await mkdtemp(join(tmpdir(), 'v1-agent-name-parity-')), 'roster.yaml');
|
||||||
|
temporaryDirectories.push(dirname(rosterPath));
|
||||||
|
await writeFile(rosterPath, source);
|
||||||
|
|
||||||
|
await expect(loadFleetRoster(rosterPath)).rejects.toThrow('Invalid fleet agent name');
|
||||||
|
|
||||||
|
const preview = await previewV1ToV2Migration({
|
||||||
|
source,
|
||||||
|
decisions: decisions(),
|
||||||
|
observations: observations(),
|
||||||
|
personaDirs: await semanticDirs(),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(preview.status).toBe('blocked');
|
||||||
|
expect(preview.blockers).toContainEqual({
|
||||||
|
code: 'invalid-v1-field-type',
|
||||||
|
path: 'agents[0].name',
|
||||||
|
detail: 'A valid agent name is required.',
|
||||||
|
});
|
||||||
|
expect(preview).not.toHaveProperty('canonicalRoster');
|
||||||
|
expect(preview).not.toHaveProperty('canonicalYaml');
|
||||||
|
expect(preview.evidence).not.toHaveProperty('candidate');
|
||||||
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
['socket_name', " socket_name: ''\n holder_session: _holder"],
|
['socket_name', " socket_name: ''\n holder_session: _holder"],
|
||||||
['socketName', " socketName: ''\n holder_session: _holder"],
|
['socketName', " socketName: ''\n holder_session: _holder"],
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ export async function previewV1ToV2Migration(
|
|||||||
|
|
||||||
for (let index = 0; index < rawAgents.length; index += 1) {
|
for (let index = 0; index < rawAgents.length; index += 1) {
|
||||||
const rawAgent = record(rawAgents[index]);
|
const rawAgent = record(rawAgents[index]);
|
||||||
const name = stringValue(rawAgent.name);
|
const name = exactStringValue(rawAgent.name);
|
||||||
const path = `agents[${index}]`;
|
const path = `agents[${index}]`;
|
||||||
if (!name) {
|
if (!name) {
|
||||||
blockers.push(blocker('invalid-agent-name', `${path}.name`, 'Agent name is required.'));
|
blockers.push(blocker('invalid-agent-name', `${path}.name`, 'Agent name is required.'));
|
||||||
@@ -538,7 +538,7 @@ export async function previewV1ToV2Migration(
|
|||||||
rawAgents
|
rawAgents
|
||||||
.map((candidate) => record(candidate))
|
.map((candidate) => record(candidate))
|
||||||
.filter((candidate) => classifyAgentLocality(candidate, decisions.fleetHost) !== 'remote')
|
.filter((candidate) => classifyAgentLocality(candidate, decisions.fleetHost) !== 'remote')
|
||||||
.map((candidate) => stringValue(candidate.name))
|
.map((candidate) => exactStringValue(candidate.name))
|
||||||
.filter((name): name is string => name !== undefined),
|
.filter((name): name is string => name !== undefined),
|
||||||
);
|
);
|
||||||
for (const decisionName of Object.keys(decisions.agents)) {
|
for (const decisionName of Object.keys(decisions.agents)) {
|
||||||
@@ -936,7 +936,7 @@ function validateV1FieldCatalog(raw: Record<string, unknown>, blockers: Migratio
|
|||||||
path,
|
path,
|
||||||
blockers,
|
blockers,
|
||||||
);
|
);
|
||||||
const name = stringValue(agent.name);
|
const name = exactStringValue(agent.name);
|
||||||
if (!name || !AGENT_NAME_PATTERN.test(name)) {
|
if (!name || !AGENT_NAME_PATTERN.test(name)) {
|
||||||
blockers.push(
|
blockers.push(
|
||||||
blocker('invalid-v1-field-type', `${path}.name`, 'A valid agent name is required.'),
|
blocker('invalid-v1-field-type', `${path}.name`, 'A valid agent name is required.'),
|
||||||
@@ -1420,6 +1420,10 @@ function record(value: unknown): Record<string, unknown> {
|
|||||||
: {};
|
: {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function exactStringValue(value: unknown): string | undefined {
|
||||||
|
return typeof value === 'string' ? value : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function trimmedString(value: unknown): string | undefined {
|
function trimmedString(value: unknown): string | undefined {
|
||||||
return typeof value === 'string' ? value.trim() : undefined;
|
return typeof value === 'string' ? value.trim() : undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user