Fix getters mutating state on repeated access

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 07:31:59 +08:00
parent ef8ce0d778
commit eeea16be38
4 changed files with 77 additions and 35 deletions

View File

@@ -124,23 +124,22 @@ export const useAllMapDataStore = defineStore('allMapDataStore', {
}, },
filterAttrs: state => { filterAttrs: state => {
if(state.allFilterAttrs !== null){ if(state.allFilterAttrs !== null){
state.allFilterAttrs.forEach(att => { return state.allFilterAttrs.map(att => {
switch (att.type) { const copy = { ...att };
switch (copy.type) {
case 'date': case 'date':
att.min = att.min !== null ? moment(att.min).format('YYYY/MM/DD HH:mm') : null; copy.min = copy.min !== null ? moment(copy.min).format('YYYY/MM/DD HH:mm') : null;
att.max = att.max !== null ? moment(att.max).format('YYYY/MM/DD HH:mm') : null; copy.max = copy.max !== null ? moment(copy.max).format('YYYY/MM/DD HH:mm') : null;
break; break;
case 'float': case 'float':
// Decimal.ROUND_UP|0: 無條件進位; Decimal.ROUND_DOWN|1: 無條件捨去。 copy.min = copy.min !== null ? Number(new Decimal(copy.min).toFixed(2, 1)) : null;
att.min = att.min !== null ? Number(new Decimal(att.min).toFixed(2, 1)) : null; copy.max = copy.max !== null ? Number(new Decimal(copy.max).toFixed(2, 0)) : null;
att.max = att.max !== null ? Number(new Decimal(att.max).toFixed(2, 0)) : null;
break break
default: default:
break; break;
} }
return att; return copy;
}); });
return state.allFilterAttrs;
} }
}, },
allFunnels: state => { allFunnels: state => {

View File

@@ -163,38 +163,42 @@ export const useConformanceStore = defineStore('conformanceStore', {
}, },
cases: state => { cases: state => {
if(state.allCases !== null){ if(state.allCases !== null){
const newData = state.allCases.map(c => { return state.allCases.map(c => {
c.started_at = moment(c.started_at).format('YYYY/MM/DD HH:mm'); const facets = c.facets.map(fac => {
c.completed_at = moment(c.completed_at).format('YYYY/MM/DD HH:mm'); const copy = { ...fac };
c.facets.forEach(fac => { switch(copy.type) {
switch(fac.type) {
case 'dummy': //sonar-qube case 'dummy': //sonar-qube
case 'duration-list': case 'duration-list':
fac.value = fac.value.map(v => v !== null ? abbreviateNumber(new Decimal(v.toFixed(2))) : null); copy.value = copy.value.map(v => v !== null ? abbreviateNumber(new Decimal(v.toFixed(2))) : null);
fac.value = (fac.value).map(v => v.trim()).join(', '); copy.value = (copy.value).map(v => v.trim()).join(', ');
break; break;
default: default:
break; break;
}; };
return fac; return copy;
}); });
c.attributes.forEach(att => { const attributes = c.attributes.map(att => {
switch (att.type) { const copy = { ...att };
switch (copy.type) {
case 'date': case 'date':
att.value = att.value !== null ? moment(att.value).format('YYYY/MM/DD HH:mm:ss') : null; copy.value = copy.value !== null ? moment(copy.value).format('YYYY/MM/DD HH:mm:ss') : null;
break; break;
case 'float': case 'float':
att.value = att.value !== null ? new Decimal(att.value).toFixed(2) : null; copy.value = copy.value !== null ? new Decimal(copy.value).toFixed(2) : null;
break break
default: default:
break; break;
} }
return att; return copy;
}); });
const { facets, attributes, ...rest } = c; return {
return { ...rest, facets, attributes }; ...c,
started_at: moment(c.started_at).format('YYYY/MM/DD HH:mm'),
completed_at: moment(c.completed_at).format('YYYY/MM/DD HH:mm'),
facets,
attributes,
};
}); });
return newData
}; };
}, },
loopTraces: state => { loopTraces: state => {
@@ -205,25 +209,28 @@ export const useConformanceStore = defineStore('conformanceStore', {
}, },
loopCases: state => { loopCases: state => {
if(state.allLoopCases !== null){ if(state.allLoopCases !== null){
const newData = state.allLoopCases.map(c => { return state.allLoopCases.map(c => {
c.started_at = moment(c.started_at).format('YYYY/MM/DD HH:mm'); const attributes = c.attributes.map(att => {
c.completed_at = moment(c.completed_at).format('YYYY/MM/DD HH:mm'); const copy = { ...att };
c.attributes.forEach(att => { switch (copy.type) {
switch (att.type) {
case 'date': case 'date':
att.value = att.value !== null ? moment(att.value).format('YYYY/MM/DD HH:mm:ss') : null; copy.value = copy.value !== null ? moment(copy.value).format('YYYY/MM/DD HH:mm:ss') : null;
break; break;
case 'float': case 'float':
att.value = att.value !== null ? new Decimal(att.value).toFixed(2) : null; copy.value = copy.value !== null ? new Decimal(copy.value).toFixed(2) : null;
break break
default: default:
break; break;
} }
return att; return copy;
}); });
return c; return {
...c,
started_at: moment(c.started_at).format('YYYY/MM/DD HH:mm'),
completed_at: moment(c.completed_at).format('YYYY/MM/DD HH:mm'),
attributes,
};
}); });
return newData;
}; };
}, },
}, },

View File

@@ -237,5 +237,28 @@ describe('allMapDataStore', () => {
store.allBaseCase = [{ id: 1 }]; store.allBaseCase = [{ id: 1 }];
expect(store.BaseInfiniteFirstCases).toBeUndefined(); expect(store.BaseInfiniteFirstCases).toBeUndefined();
}); });
it('filterAttrs getter does not corrupt original state on repeated access', () => {
const originalDate = '2023-06-15T10:30:00Z';
store.allFilterAttrs = [
{ type: 'date', min: originalDate, max: '2023-12-31T23:59:00Z' },
];
// Access the getter once
store.filterAttrs;
// The original state should still contain the ISO date, not a formatted string
expect(store.allFilterAttrs[0].min).toBe(originalDate);
});
it('filterAttrs getter returns consistent float values on repeated access', () => {
store.allFilterAttrs = [
{ type: 'float', min: 1.239, max: 5.671 },
];
const first = store.filterAttrs;
const second = store.filterAttrs;
// min rounds down (ROUND_DOWN=1), max rounds up (ROUND_UP=0)
// Both accesses should produce the same result
expect(first[0].min).toBe(second[0].min);
expect(first[0].max).toBe(second[0].max);
});
}); });
}); });

View File

@@ -196,5 +196,18 @@ describe('conformanceStore', () => {
// The date attribute should show minutes (30), not month (06) // The date attribute should show minutes (30), not month (06)
expect(result[0].attributes[0].value).toMatch(/:30:/); expect(result[0].attributes[0].value).toMatch(/:30:/);
}); });
it('cases getter does not corrupt original state on repeated access', () => {
const originalDate = '2023-06-15T10:30:00Z';
store.allCases = [{
started_at: originalDate,
completed_at: '2023-06-15T11:45:00Z',
facets: [],
attributes: [],
}];
store.cases;
// Original state should still contain the ISO date
expect(store.allCases[0].started_at).toBe(originalDate);
});
}); });
}); });