feat(data-compare): support recompare and endpoint distinction
This commit is contained in:
12 files changed
+68
-6
No files matched your search
@@ -32,7 +32,7 @@ import {
|
||||
import * as api from "@/lib/backend/api";
|
||||
import { executeWithProductionSqlGuard } from "@/lib/database/productionExecutionGuard";
|
||||
import TableMultiSelect from "@/components/diff/TableMultiSelect.vue";
|
||||
import { ArrowLeftRight, CheckSquare, ChevronDown, ChevronRight, Copy, GitCompareArrows, Loader2, Play, Square } from "@lucide/vue";
|
||||
import { ArrowLeftRight, CheckSquare, ChevronDown, ChevronRight, Copy, GitCompareArrows, Loader2, Play, RotateCcw, Square } from "@lucide/vue";
|
||||
|
||||
const PREVIEW_LIMIT_OPTIONS = [50, 100, 200, 500];
|
||||
const SYNC_EXECUTE_BATCH_SIZE = 500;
|
||||
@@ -510,7 +510,7 @@ async function rebuildSyncPlan() {
|
||||
}
|
||||
|
||||
function startCompare(): void {
|
||||
if (!canCompare.value || comparing.value) return;
|
||||
if (!canCompare.value || comparing.value || executing.value) return;
|
||||
const tasks = buildCompareTasks();
|
||||
if (tasks.length === 0) {
|
||||
toast(t("dataCompare.noComparableTables"), 5000);
|
||||
@@ -785,8 +785,11 @@ onBeforeUnmount(() => {
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-auto space-y-4 py-2">
|
||||
<div class="grid grid-cols-[1fr_auto_1fr] gap-4 items-start">
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs font-medium">{{ t("diff.source") }}</Label>
|
||||
<div class="space-y-2 rounded-lg border border-blue-500/35 bg-blue-500/5 p-3">
|
||||
<div class="flex items-center gap-2 text-sm font-medium text-blue-600 dark:text-blue-400">
|
||||
<span class="inline-flex h-5 w-5 items-center justify-center rounded-full bg-blue-500/15 text-[11px] font-semibold">S</span>
|
||||
{{ t("diff.source") }}
|
||||
</div>
|
||||
<ConnectionTreeSelect
|
||||
v-model="sourceConnectionId"
|
||||
:disabled="comparing"
|
||||
@@ -838,8 +841,11 @@ onBeforeUnmount(() => {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs font-medium">{{ t("diff.target") }}</Label>
|
||||
<div class="space-y-2 rounded-lg border border-emerald-500/35 bg-emerald-500/5 p-3">
|
||||
<div class="flex items-center gap-2 text-sm font-medium text-emerald-600 dark:text-emerald-400">
|
||||
<span class="inline-flex h-5 w-5 items-center justify-center rounded-full bg-emerald-500/15 text-[11px] font-semibold">T</span>
|
||||
{{ t("diff.target") }}
|
||||
</div>
|
||||
<ConnectionTreeSelect
|
||||
v-model="targetConnectionId"
|
||||
:disabled="comparing"
|
||||
@@ -1110,6 +1116,11 @@ onBeforeUnmount(() => {
|
||||
|
||||
<DialogFooter v-else class="flex items-center gap-2">
|
||||
<Button variant="outline" @click="open = false">{{ t("common.close") }}</Button>
|
||||
<Button variant="outline" size="sm" :disabled="comparing || executing || !canCompare" @click="startCompare">
|
||||
<Loader2 v-if="comparing" class="w-3 h-3 animate-spin mr-1" />
|
||||
<RotateCcw v-else class="w-3 h-3 mr-1" />
|
||||
{{ t("dataCompare.recompare") }}
|
||||
</Button>
|
||||
<span v-if="comparing" class="text-xs text-muted-foreground mr-auto">{{ compareProgressLabel || t("diff.progress.comparing") }}</span>
|
||||
<span v-else-if="executing" class="text-xs text-muted-foreground mr-auto">
|
||||
{{ t("diff.syncProgress", { current: executedCount, total: executeTotal }) }}
|
||||
|
||||
@@ -13,6 +13,7 @@ const mocks = vi.hoisted(() => ({
|
||||
listTables: vi.fn().mockResolvedValue([{ name: "CODEX_7467_META", table_type: "TABLE" }]),
|
||||
getColumns: vi.fn().mockResolvedValue([{ name: "ID", data_type: "NUMBER", is_primary_key: true }]),
|
||||
buildDataCompareSyncPlan: vi.fn(),
|
||||
executeBatch: vi.fn(),
|
||||
}));
|
||||
|
||||
const sessionMocks = vi.hoisted(() => ({
|
||||
@@ -58,6 +59,7 @@ vi.mock("@/lib/backend/api", () => ({
|
||||
listTables: mocks.listTables,
|
||||
getColumns: mocks.getColumns,
|
||||
buildDataCompareSyncPlan: mocks.buildDataCompareSyncPlan,
|
||||
executeBatch: mocks.executeBatch,
|
||||
}));
|
||||
|
||||
const mountedApps: App[] = [];
|
||||
@@ -259,6 +261,45 @@ describe("DataCompareDialog session restore", () => {
|
||||
expect(document.querySelector<HTMLTextAreaElement>("textarea[readonly]")?.value).toBe("INSERT 1;\nINSERT 2;");
|
||||
});
|
||||
|
||||
it("starts another comparison and distinguishes source and target panels after results", async () => {
|
||||
mountSessionDialog(completedSession());
|
||||
|
||||
await flushAsyncSetup();
|
||||
|
||||
const recompareButton = [...document.querySelectorAll<HTMLButtonElement>("button")].find((button) => button.textContent?.includes(i18n.global.t("dataCompare.recompare")));
|
||||
expect(recompareButton).toBeDefined();
|
||||
expect([...document.querySelectorAll("div")].filter((element) => element.classList.contains("border-blue-500/35"))).toHaveLength(1);
|
||||
expect([...document.querySelectorAll("div")].filter((element) => element.classList.contains("border-emerald-500/35"))).toHaveLength(1);
|
||||
|
||||
recompareButton?.click();
|
||||
await flushAsyncSetup();
|
||||
|
||||
expect(mocks.ensureConnected).toHaveBeenCalledWith("oracle-11g");
|
||||
expect(mocks.ensureConnected).toHaveBeenCalledWith("oracle-jdbc-11g");
|
||||
});
|
||||
|
||||
it("disables recompare while sync SQL is still executing against the target", async () => {
|
||||
mocks.executeBatch.mockImplementation(() => new Promise(() => {}));
|
||||
mountSessionDialog(completedSession());
|
||||
|
||||
await flushAsyncSetup();
|
||||
mocks.ensureConnected.mockClear();
|
||||
|
||||
const executeButton = [...document.querySelectorAll<HTMLButtonElement>("button")].find((button) => button.textContent?.includes(i18n.global.t("diff.executeSync")));
|
||||
expect(executeButton).toBeDefined();
|
||||
executeButton?.click();
|
||||
await flushAsyncSetup();
|
||||
|
||||
const recompareButton = [...document.querySelectorAll<HTMLButtonElement>("button")].find((button) => button.textContent?.includes(i18n.global.t("dataCompare.recompare")));
|
||||
expect(recompareButton).toBeDefined();
|
||||
expect(recompareButton?.disabled).toBe(true);
|
||||
|
||||
recompareButton?.click();
|
||||
await flushAsyncSetup();
|
||||
|
||||
expect(mocks.ensureConnected).not.toHaveBeenCalledWith("oracle-11g");
|
||||
});
|
||||
|
||||
it("persists the latest selection plan when the dialog closes during planning", async () => {
|
||||
const session = completedSession();
|
||||
let resolvePlan!: (plan: DataCompareSession["syncPlan"]) => void;
|
||||
|
||||
@@ -6633,6 +6633,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "Mənbədə {source} sətir, hədəfdə {target} sətir; tam müqayisə tamamlandı",
|
||||
previewLimitOption: "Önbaxış: {count}",
|
||||
compare: "Verilənləri müqayisə et",
|
||||
recompare: "Yenidən müqayisə et",
|
||||
summary: "Əlavə edilib: {added}, çıxarılıb: {removed}, dəyişdirilib: {modified}",
|
||||
batchSummary: "{tables} cədvəl müqayisə edilib: {different} fərqli, {same} eyni, {failed} uğursuz; əlavə edilib: {added}, çıxarılıb: {removed}, dəyişdirilib: {modified}",
|
||||
comparingTable: "Müqayisə edilir {current}/{total}: {table}",
|
||||
|
||||
@@ -6820,6 +6820,7 @@ export default {
|
||||
rowCounts: "Source {source} rows, target {target} rows; full compare completed",
|
||||
previewLimitOption: "Preview {count}",
|
||||
compare: "Compare Data",
|
||||
recompare: "Compare Again",
|
||||
summary: "Added {added}, removed {removed}, modified {modified}",
|
||||
batchSummary: "Compared {tables} tables: {different} different, {same} identical, {failed} failed; added {added}, removed {removed}, modified {modified}",
|
||||
comparingTable: "Comparing {current}/{total}: {table}",
|
||||
|
||||
@@ -6393,6 +6393,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "Origen {source} filas, destino {target} filas; comparación completa terminada",
|
||||
previewLimitOption: "Vista previa {count}",
|
||||
compare: "Comparar datos",
|
||||
recompare: "Comparar de nuevo",
|
||||
summary: "Agregadas {added}, eliminadas {removed}, modificadas {modified}",
|
||||
batchSummary: "Se compararon {tables} tablas: {different} con diferencias, {same} iguales, {failed} con error; agregadas {added}, eliminadas {removed}, modificadas {modified}",
|
||||
comparingTable: "Comparando {current}/{total}: {table}",
|
||||
|
||||
@@ -6393,6 +6393,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "Sorgente {source} righe, destinazione {target} righe; confronto completo completato",
|
||||
previewLimitOption: "Anteprima {count}",
|
||||
compare: "Confronta Dati",
|
||||
recompare: "Confronta di nuovo",
|
||||
summary: "Aggiunte {added}, rimosse {removed}, modificate {modified}",
|
||||
batchSummary: "Confrontate {tables} tabelle: {different} diverse, {same} identiche, {failed} non riuscite; aggiunte {added}, rimosse {removed}, modificate {modified}",
|
||||
comparingTable: "Confronto di {current}/{total}: {table}",
|
||||
|
||||
@@ -6431,6 +6431,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "ソース {source}行、ターゲット {target}行、完全比較が完了しました",
|
||||
previewLimitOption: "{count}件プレビュー",
|
||||
compare: "データを比較",
|
||||
recompare: "再比較",
|
||||
summary: "追加 {added}、削除 {removed}、変更 {modified}",
|
||||
batchSummary: "{tables}テーブルを比較: {different}不一致、{same}一致、{failed}失敗; 追加 {added}、削除 {removed}、変更 {modified}",
|
||||
comparingTable: "比較中 {current}/{total}: {table}",
|
||||
|
||||
@@ -6143,6 +6143,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "소스 {source}행, 대상 {target}행; 전체 비교 완료",
|
||||
previewLimitOption: "미리보기 {count}",
|
||||
compare: "데이터 비교",
|
||||
recompare: "다시 비교",
|
||||
summary: "추가 {added}, 제거 {removed}, 수정 {modified}",
|
||||
batchSummary: "테이블 {tables}개 비교: {different}개 차이, {same}개 동일, {failed}개 실패; 추가 {added}, 제거 {removed}, 수정 {modified}",
|
||||
comparingTable: "{current}/{total} 비교 중: {table}",
|
||||
|
||||
@@ -6395,6 +6395,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "Origem com {source} linhas, destino com {target} linhas; comparação completa concluída",
|
||||
previewLimitOption: "Pré-visualizar {count}",
|
||||
compare: "Comparar Dados",
|
||||
recompare: "Comparar novamente",
|
||||
summary: "{added} adicionados, {removed} removidos, {modified} modificados",
|
||||
batchSummary: "{tables} tabelas comparadas: {different} diferentes, {same} idênticas, {failed} com falha; {added} adicionados, {removed} removidos, {modified} modificados",
|
||||
comparingTable: "Comparando {current}/{total}: {table}",
|
||||
|
||||
@@ -6515,6 +6515,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "Kaynak {source} satır, hedef {target} satır; tam karşılaştırma tamamlandı",
|
||||
previewLimitOption: "Önizleme {count}",
|
||||
compare: "Verileri Karşılaştır",
|
||||
recompare: "Tekrar karşılaştır",
|
||||
summary: "{added} eklendi, {removed} kaldırıldı, {modified} değiştirildi",
|
||||
batchSummary: "{tables} tablo karşılaştırıldı: {different} farklı, {same} aynı, {failed} başarısız; {added} eklendi, {removed} kaldırıldı, {modified} değiştirildi",
|
||||
comparingTable: "Karşılaştırılıyor {current}/{total}: {table}",
|
||||
|
||||
@@ -6794,6 +6794,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "源表 {source} 行,目标表 {target} 行;已完成全量比较",
|
||||
previewLimitOption: "预览 {count}",
|
||||
compare: "开始比较数据",
|
||||
recompare: "重新比较",
|
||||
summary: "新增 {added} 行,删除 {removed} 行,修改 {modified} 行",
|
||||
batchSummary: "已比较 {tables} 张表:存在差异 {different} 张,一致 {same} 张,失败 {failed} 张;累计新增 {added} 行,删除 {removed} 行,修改 {modified} 行",
|
||||
comparingTable: "正在比较 {current}/{total}:{table}",
|
||||
|
||||
@@ -5713,6 +5713,7 @@ export default withEnglishFallback({
|
||||
rowCounts: "來源 {source} 列,目標 {target} 列;已完成完整比較",
|
||||
previewLimitOption: "預覽 {count}",
|
||||
compare: "開始比較資料",
|
||||
recompare: "重新比較",
|
||||
summary: "新增 {added} 列,刪除 {removed} 列,修改 {modified} 列",
|
||||
batchSummary: "已比較 {tables} 張資料表:{different} 張有差異,{same} 張一致,{failed} 張失敗;新增 {added} 列,刪除 {removed} 列,修改 {modified} 列",
|
||||
comparingTable: "正在比較 {current}/{total}:{table}",
|
||||
|
||||
Reference in new issue
Block a user