Files
MikrotikManager/lib/user-save.test.ts
T

34 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from "node:test"
import assert from "node:assert/strict"
import { saveUserSteps, type UserSaveCheckpoint, type UserSaveApi } from "./user-save"
import { ApiClientError } from "@/shared/api/http-client"
import type { AppUser, AppUserForm, InterfaceBinding } from "./users"
const user:AppUser={id:"u1",name:"Тест",login:"[email protected]",email:"[email protected]",role:"viewer",active:true,avatar:"Т",last:"—",sections:[],servers:[],bindings:[]}
const binding=(name:string):InterfaceBinding=>({id:"draft-"+name,userId:"",serverId:"1",serverName:"Сервер",serverSite:"",serverCountry:"RU",interfaceName:name,interfaceType:"ether",comment:""})
const form:AppUserForm={...user,bindings:[binding("ether1"),binding("ether2")]}
function api(overrides:Partial<UserSaveApi>={}):UserSaveApi{return{create:async()=>({...user}),update:async()=>({...user}),read:async()=>({...user}),removeBinding:async()=>{},addBinding:async(_,b)=>({...b,id:"saved-"+b.interfaceName,userId:"u1"}),...overrides}}
test("частичные привязки: повтор читает сервер, не создаёт второй профиль и не дублирует привязку",async()=>{
const state:UserSaveCheckpoint={user:null},calls:string[]=[];let fail=true,remote:AppUser={...user}
const service=api({create:async()=>{calls.push("create");return remote},read:async()=>{calls.push("read");return remote},update:async()=>{calls.push("patch");return remote},addBinding:async(_,b)=>{calls.push("add:"+b.interfaceName);if(b.interfaceName==="ether2"&&fail)throw new ApiClientError("conflict",409);const saved={...b,id:"saved-"+b.interfaceName,userId:"u1"};remote={...remote,bindings:[...remote.bindings,saved]};return saved}})
await assert.rejects(saveUserSteps(form,state,service,()=>true,()=>{}),/conflict/);assert.equal(state.user?.bindings.length,1);assert.equal(state.bindingsNeedRead,true)
fail=false;const saved=await saveUserSteps(form,state,service,()=>true,()=>{});assert.equal(saved?.bindings.length,2);assert.deepEqual(calls,["create","add:ether1","add:ether2","read","patch","add:ether2"])
})
test("потерянный ответ удаления: read подтверждает удаление, повтор не отправляет второй DELETE",async()=>{
const b={...binding("ether1"),id:"old",userId:"u1"};let remote:AppUser={...user,bindings:[b]},deletes=0
const state:UserSaveCheckpoint={user:remote},service=api({update:async()=>remote,read:async()=>remote,removeBinding:async()=>{deletes++;remote={...remote,bindings:[]};throw new Error("socket closed")}})
await assert.rejects(saveUserSteps({...form,bindings:[]},state,service,()=>true,()=>{}),/socket/);await saveUserSteps({...form,bindings:[]},state,service,()=>true,()=>{});assert.equal(deletes,1);assert.equal(state.user?.bindings.length,0)
})
test("неизвестный исход создания запрещает повторный POST; 403 допускает исправление",async()=>{
for(const known of [false,true]){let creates=0;const state:UserSaveCheckpoint={user:null};const service=api({create:async()=>{creates++;throw new ApiClientError("fail",known?403:500)}})
await assert.rejects(saveUserSteps(form,state,service,()=>true,()=>{}));assert.equal(!!state.createUncertain,!known);await assert.rejects(saveUserSteps(form,state,service,()=>true,()=>{}));assert.equal(creates,known?2:1)}
})
test("поздний профиль прежнего контекста не начинает следующие сетевые шаги",async()=>{
let active=true,adds=0,changes=0;const state:UserSaveCheckpoint={user:null},service=api({create:async()=>{active=false;return user},addBinding:async(_,b)=>{adds++;return b}})
assert.equal(await saveUserSteps(form,state,service,()=>active,()=>{changes++}),null);assert.equal(adds,0);assert.equal(changes,0);assert.equal(state.user,null)
})
test("изменённый профиль после частичного шага PATCH идёт по подтверждённому id",async()=>{
const state:UserSaveCheckpoint={user,profileSignature:"previous"};let id=""
const service=api({update:async(userId,f)=>{id=userId;return{...user,name:f.name}}})
const saved=await saveUserSteps({...form,name:"Новое имя",bindings:[]},state,service,()=>true,()=>{});assert.equal(id,"u1");assert.equal(saved?.name,"Новое имя")
})