192 lines
5.7 KiB
TypeScript
Vendored
192 lines
5.7 KiB
TypeScript
Vendored
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { createPinia, setActivePinia } from "pinia";
|
|
import { ref } from "vue";
|
|
import { runMongoSidebarMutation } from "../runMongoSidebarMutation";
|
|
import { useProductionSafetyStore } from "@/stores/productionSafetyStore";
|
|
import type { ConnectionConfig } from "@/types/database";
|
|
|
|
function connection(overrides: Partial<ConnectionConfig> = {}): ConnectionConfig {
|
|
return {
|
|
id: "conn-1",
|
|
name: "Mongo",
|
|
db_type: "mongodb",
|
|
host: "localhost",
|
|
port: 27017,
|
|
username: "op",
|
|
password: "",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("runMongoSidebarMutation", () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia());
|
|
});
|
|
|
|
it("calls onSuccess for void execute results (not treated as cancel)", async () => {
|
|
const loading = ref(false);
|
|
const execute = vi.fn().mockResolvedValue(undefined);
|
|
const onSuccess = vi.fn();
|
|
const beforeExecute = vi.fn().mockResolvedValue(undefined);
|
|
|
|
await runMongoSidebarMutation({
|
|
connection: connection(),
|
|
database: "app",
|
|
reviewText: 'db.getCollection("users").renameCollection("accounts")',
|
|
source: "Object tree",
|
|
loading,
|
|
beforeExecute,
|
|
execute,
|
|
onSuccess,
|
|
});
|
|
|
|
expect(beforeExecute).toHaveBeenCalledTimes(1);
|
|
expect(execute).toHaveBeenCalledTimes(1);
|
|
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
expect(onSuccess).toHaveBeenCalledWith(undefined);
|
|
expect(loading.value).toBe(false);
|
|
});
|
|
|
|
it("passes through non-void execute results to onSuccess", async () => {
|
|
const loading = ref(false);
|
|
const result = { dropped_names: ["a", "b"] };
|
|
const onSuccess = vi.fn();
|
|
|
|
await runMongoSidebarMutation({
|
|
connection: connection(),
|
|
database: "app",
|
|
reviewText: 'db.getCollection("users").dropIndexes()',
|
|
source: "Object tree",
|
|
loading,
|
|
execute: async () => result,
|
|
onSuccess,
|
|
});
|
|
|
|
expect(onSuccess).toHaveBeenCalledWith(result);
|
|
});
|
|
|
|
it("waits for asynchronous success work before clearing loading", async () => {
|
|
const loading = ref(false);
|
|
let releaseSuccess: () => void = () => {};
|
|
const successWork = new Promise<void>((resolve) => {
|
|
releaseSuccess = resolve;
|
|
});
|
|
const onSuccess = vi.fn(async () => successWork);
|
|
|
|
const pending = runMongoSidebarMutation({
|
|
connection: connection(),
|
|
database: "app",
|
|
reviewText: 'db.getCollection("users").createIndex({"email":1})',
|
|
source: "Object tree",
|
|
loading,
|
|
execute: async () => ({ name: "email_1" }),
|
|
onSuccess,
|
|
});
|
|
|
|
await vi.waitFor(() => expect(onSuccess).toHaveBeenCalledOnce());
|
|
expect(loading.value).toBe(true);
|
|
releaseSuccess();
|
|
await pending;
|
|
expect(loading.value).toBe(false);
|
|
});
|
|
|
|
it("does not execute or call onSuccess when production confirmation is cancelled", async () => {
|
|
const loading = ref(false);
|
|
const execute = vi.fn().mockResolvedValue(undefined);
|
|
const onSuccess = vi.fn();
|
|
const beforeExecute = vi.fn();
|
|
|
|
const pending = runMongoSidebarMutation({
|
|
connection: connection({ is_production: true }),
|
|
database: "app",
|
|
reviewText: 'db.getCollection("users").drop()',
|
|
source: "Object tree",
|
|
loading,
|
|
beforeExecute,
|
|
execute,
|
|
onSuccess,
|
|
});
|
|
|
|
await Promise.resolve();
|
|
expect(useProductionSafetyStore().pending).toBeTruthy();
|
|
expect(loading.value).toBe(false);
|
|
expect(execute).not.toHaveBeenCalled();
|
|
|
|
useProductionSafetyStore().cancel();
|
|
await pending;
|
|
|
|
expect(beforeExecute).not.toHaveBeenCalled();
|
|
expect(execute).not.toHaveBeenCalled();
|
|
expect(onSuccess).not.toHaveBeenCalled();
|
|
expect(loading.value).toBe(false);
|
|
});
|
|
|
|
it("executes after production confirmation", async () => {
|
|
const loading = ref(false);
|
|
const execute = vi.fn().mockResolvedValue(undefined);
|
|
const onSuccess = vi.fn();
|
|
|
|
const pending = runMongoSidebarMutation({
|
|
connection: connection({ is_production: true }),
|
|
database: "app",
|
|
reviewText: 'db.getCollection("users").drop()',
|
|
source: "Object tree",
|
|
loading,
|
|
execute,
|
|
onSuccess,
|
|
});
|
|
|
|
await Promise.resolve();
|
|
useProductionSafetyStore().confirm();
|
|
await pending;
|
|
|
|
expect(execute).toHaveBeenCalledTimes(1);
|
|
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("routes existing-target reject to onError without onSuccess", async () => {
|
|
// Mongo rename of an existing target fails at the server (no dropTarget).
|
|
const loading = ref(false);
|
|
const onError = vi.fn();
|
|
const onSuccess = vi.fn();
|
|
const existingTargetError = new Error("Command failed: Namespace app.accounts already exists. target namespace exists");
|
|
|
|
await runMongoSidebarMutation({
|
|
connection: connection(),
|
|
database: "app",
|
|
reviewText: 'db.getSiblingDB("app").getCollection("users").renameCollection("accounts")',
|
|
source: "Object tree",
|
|
loading,
|
|
execute: async () => {
|
|
throw existingTargetError;
|
|
},
|
|
onSuccess,
|
|
onError,
|
|
});
|
|
|
|
expect(onSuccess).not.toHaveBeenCalled();
|
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
expect(onError.mock.calls[0]?.[0]).toBe(existingTargetError);
|
|
expect(loading.value).toBe(false);
|
|
});
|
|
|
|
it("skips work when already loading", async () => {
|
|
const loading = ref(true);
|
|
const execute = vi.fn();
|
|
const onSuccess = vi.fn();
|
|
|
|
await runMongoSidebarMutation({
|
|
connection: connection(),
|
|
database: "app",
|
|
reviewText: "drop",
|
|
source: "Object tree",
|
|
loading,
|
|
execute,
|
|
onSuccess,
|
|
});
|
|
|
|
expect(execute).not.toHaveBeenCalled();
|
|
expect(onSuccess).not.toHaveBeenCalled();
|
|
});
|
|
});
|