Nacti() uložilo dodávanou vrstvu do referenčního snímku _vychozi a tytéž objekty vložilo do kolekcí, na které se váže mřížka. Úprava buňky tak mutovala i ten snímek – UlozRozdil pak porovnával stav sám se sebou, rozdíl vyšel prázdný a oprava dodávané položky zmizela. Mazání a přidávání přitom fungovalo (tam se reference nesdílí), takže se chyba dobře schovala. Sluc nově vkládá dodávané položky jako hluboké kopie (serializací, stejně jako klonování přílohy), takže snímek zůstane netknutý. Nahlášeno na přejmenování kraje Karlovarský – přesně ten scénář teď hlídají dva regresní testy; bez opravy oba padají. Build 0 chyb / 0 varování, 57 testů zelených. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
235 lines
9.0 KiB
C#
235 lines
9.0 KiB
C#
using System.Collections.ObjectModel;
|
||
using Rozpisky.Data;
|
||
using Rozpisky.Models;
|
||
using Xunit;
|
||
|
||
namespace Rozpisky.Tests;
|
||
|
||
/// <summary>
|
||
/// Slučování dodávané a uživatelské vrstvy číselníků a dopočet rozdílu
|
||
/// (<see cref="CiselnikyStore.Sluc{T}"/> / <see cref="CiselnikyStore.Rozdil{T}"/>).
|
||
/// Na téhle logice stojí, jestli uživatel po aktualizaci programu najde svoje záznamy –
|
||
/// chyba tady se projeví tichou ztrátou dat, kterou nikdo nenahlásí.
|
||
/// </summary>
|
||
public class CiselnikyVrstvyTesty
|
||
{
|
||
private static List<Jmeno> Vychozi(params string[] jmena)
|
||
=> jmena.Select(j => new Jmeno { Hodnota = j }).ToList();
|
||
|
||
private static ObservableCollection<Jmeno> Sluc(
|
||
List<Jmeno> vychozi, CiselnikyStore.Vrstva<Jmeno>? uziv)
|
||
{
|
||
var cil = new ObservableCollection<Jmeno>();
|
||
CiselnikyStore.Sluc(cil, vychozi, uziv);
|
||
return cil;
|
||
}
|
||
|
||
[Fact]
|
||
public void BezUzivatelskeVrstvyZustaneDodavanaBezeZmeny()
|
||
{
|
||
var vysledek = Sluc(Vychozi("Novák", "Svoboda"), null);
|
||
Assert.Equal(new[] { "Novák", "Svoboda" }, vysledek.Select(j => j.Hodnota));
|
||
}
|
||
|
||
[Fact]
|
||
public void VlastniPolozkySeRadiZaDodavane()
|
||
{
|
||
var uziv = new CiselnikyStore.Vrstva<Jmeno> { Vlastni = Vychozi("Dvořák") };
|
||
var vysledek = Sluc(Vychozi("Novák", "Svoboda"), uziv);
|
||
Assert.Equal(new[] { "Novák", "Svoboda", "Dvořák" }, vysledek.Select(j => j.Hodnota));
|
||
}
|
||
|
||
[Fact]
|
||
public void SkrytaDodavanaPolozkaSeNeobjevi()
|
||
{
|
||
var uziv = new CiselnikyStore.Vrstva<Jmeno> { Skryte = { "Svoboda" } };
|
||
var vysledek = Sluc(Vychozi("Novák", "Svoboda"), uziv);
|
||
Assert.Equal(new[] { "Novák" }, vysledek.Select(j => j.Hodnota));
|
||
}
|
||
|
||
[Fact]
|
||
public void UpravenaDodavanaPolozkaSePrepiseNaSvemMiste()
|
||
{
|
||
// Uživatel opravil adresu dodávané organizace – musí zůstat na původní pozici,
|
||
// jinak by se mu po každé aktualizaci přeházelo pořadí v našeptávači.
|
||
var vychozi = new List<Organizace>
|
||
{
|
||
new() { Nazev = "SŽ Praha", Adresa = "stará" },
|
||
new() { Nazev = "SŽ Brno", Adresa = "Kounicova 26" },
|
||
};
|
||
var uziv = new CiselnikyStore.Vrstva<Organizace>
|
||
{
|
||
Vlastni = { new Organizace { Nazev = "SŽ Praha", Adresa = "Dlážděná 1003/7" } },
|
||
};
|
||
|
||
var cil = new ObservableCollection<Organizace>();
|
||
CiselnikyStore.Sluc(cil, vychozi, uziv);
|
||
|
||
Assert.Equal(new[] { "SŽ Praha", "SŽ Brno" }, cil.Select(o => o.Nazev));
|
||
Assert.Equal("Dlážděná 1003/7", cil[0].Adresa);
|
||
}
|
||
|
||
[Fact]
|
||
public void ParovaniIgnorujeVelikostPismen()
|
||
{
|
||
var uziv = new CiselnikyStore.Vrstva<Jmeno> { Vlastni = Vychozi("NOVÁK") };
|
||
var vysledek = Sluc(Vychozi("Novák"), uziv);
|
||
Assert.Single(vysledek);
|
||
Assert.Equal("NOVÁK", vysledek[0].Hodnota);
|
||
}
|
||
|
||
[Fact]
|
||
public void RozepsaneRadkySeNeslijouDoJednoho()
|
||
{
|
||
// Dva prázdné řádky přidané tlačítkem „Přidat záznam“ mají stejný (prázdný) klíč –
|
||
// nesmí se spárovat, jinak by uživateli jeden zmizel dřív, než ho stihne vyplnit.
|
||
var uziv = new CiselnikyStore.Vrstva<Jmeno> { Vlastni = Vychozi("", "") };
|
||
var vysledek = Sluc(Vychozi("Novák"), uziv);
|
||
Assert.Equal(3, vysledek.Count);
|
||
}
|
||
|
||
[Fact]
|
||
public void RozdilZachytiPridaniUpravuISkryti()
|
||
{
|
||
var vychozi = Vychozi("Novák", "Svoboda", "Černý");
|
||
// Uživatel: Svobodu smazal, přidal Dvořáka, Novák zůstal beze změny.
|
||
var stav = Vychozi("Novák", "Černý", "Dvořák");
|
||
|
||
var rozdil = CiselnikyStore.Rozdil(stav, vychozi);
|
||
|
||
Assert.Equal(new[] { "Dvořák" }, rozdil.Vlastni.Select(j => j.Hodnota));
|
||
Assert.Equal(new[] { "Svoboda" }, rozdil.Skryte);
|
||
}
|
||
|
||
[Fact]
|
||
public void RozdilNepovazujeNezmenenouPolozkuZaVlastni()
|
||
{
|
||
var vychozi = Vychozi("Novák", "Svoboda");
|
||
var rozdil = CiselnikyStore.Rozdil(Vychozi("Novák", "Svoboda"), vychozi);
|
||
|
||
Assert.Empty(rozdil.Vlastni);
|
||
Assert.Empty(rozdil.Skryte);
|
||
}
|
||
|
||
[Fact]
|
||
public void RozdilZachytiZmenuJinehoNezKlicovehoPole()
|
||
{
|
||
var vychozi = new List<Firma>
|
||
{
|
||
new() { Nazev = "EXprojekt s.r.o.", Telefon = "+420 533 312 000" },
|
||
};
|
||
var stav = new List<Firma>
|
||
{
|
||
new() { Nazev = "EXprojekt s.r.o.", Telefon = "+420 111 222 333" },
|
||
};
|
||
|
||
var rozdil = CiselnikyStore.Rozdil(stav, vychozi);
|
||
|
||
Assert.Single(rozdil.Vlastni);
|
||
Assert.Equal("+420 111 222 333", rozdil.Vlastni[0].Telefon);
|
||
Assert.Empty(rozdil.Skryte); // klíč zůstal, nejde o skrytí
|
||
}
|
||
|
||
[Fact]
|
||
public void PrejmenovaniDodavanePolozkySeUloziJakoSkrytiAPridani()
|
||
{
|
||
var vychozi = Vychozi("Nowak");
|
||
var rozdil = CiselnikyStore.Rozdil(Vychozi("Novák"), vychozi);
|
||
|
||
Assert.Equal(new[] { "Novák" }, rozdil.Vlastni.Select(j => j.Hodnota));
|
||
Assert.Equal(new[] { "Nowak" }, rozdil.Skryte);
|
||
}
|
||
|
||
[Fact]
|
||
public void RozdilASlouceniDavajiZpetPuvodniStav()
|
||
{
|
||
// Kolečko uložit → načíst nesmí nic ztratit ani přidat, ať se s číselníkem stalo cokoli.
|
||
var vychozi = Vychozi("Novák", "Svoboda", "Černý");
|
||
var stav = Vychozi("Novák", "Černý", "Dvořák", "Horák");
|
||
|
||
var vysledek = Sluc(vychozi, CiselnikyStore.Rozdil(stav, vychozi));
|
||
|
||
Assert.Equal(stav.Select(j => j.Hodnota), vysledek.Select(j => j.Hodnota));
|
||
}
|
||
|
||
[Fact]
|
||
public void NovaDodavanaPolozkaSePoAktualizaciObjeviAVlastniZustanou()
|
||
{
|
||
// Jádro celé změny: program vydá novou verzi s doplněným číselníkem, uživatelův
|
||
// rozdíl je pořád ten starý – nová položka musí přibýt, vlastní nesmí zmizet.
|
||
var stareVychozi = Vychozi("Novák");
|
||
var rozdilUzivatele = CiselnikyStore.Rozdil(Vychozi("Novák", "Dvořák"), stareVychozi);
|
||
|
||
var noveVychozi = Vychozi("Novák", "Svoboda");
|
||
var vysledek = Sluc(noveVychozi, rozdilUzivatele);
|
||
|
||
Assert.Equal(new[] { "Novák", "Svoboda", "Dvořák" }, vysledek.Select(j => j.Hodnota));
|
||
}
|
||
|
||
[Fact]
|
||
public void SkrytiPrezijeAktualizaciDodavaneVrstvy()
|
||
{
|
||
var stareVychozi = Vychozi("Novák", "Svoboda");
|
||
var rozdilUzivatele = CiselnikyStore.Rozdil(Vychozi("Novák"), stareVychozi);
|
||
|
||
// Nová verze programu Svobodu pořád dodává – uživatel ho ale jednou odklidil.
|
||
var vysledek = Sluc(Vychozi("Novák", "Svoboda", "Černý"), rozdilUzivatele);
|
||
|
||
Assert.Equal(new[] { "Novák", "Černý" }, vysledek.Select(j => j.Hodnota));
|
||
}
|
||
|
||
[Fact]
|
||
public void UpravaDodavanePolozkyVMrizceSeZapiseDoRozdilu()
|
||
{
|
||
// Regrese: dodávané položky se dřív do mřížky vkládaly jako tytéž objekty, jaké držel
|
||
// referenční snímek dodávané vrstvy. Úprava v mřížce mutovala i snímek, rozdíl se pak
|
||
// počítal proti už změněným datům, vyšel prázdný a oprava se tiše ztratila.
|
||
// Mazání a přidávání přitom fungovalo, takže se to snadno přehlédne.
|
||
var dodavana = new List<Kraj>
|
||
{
|
||
new() { Nazev = "Jihomoravský" },
|
||
new() { Nazev = "Karlovarský" },
|
||
};
|
||
|
||
var vMrizce = new ObservableCollection<Kraj>();
|
||
CiselnikyStore.Sluc(vMrizce, dodavana, null);
|
||
|
||
// Uživatel přepíše buňku v mřížce.
|
||
vMrizce[1].Nazev = "Karlovarskýýýýý";
|
||
|
||
var rozdil = CiselnikyStore.Rozdil(vMrizce, dodavana);
|
||
|
||
Assert.Equal(new[] { "Karlovarskýýýýý" }, rozdil.Vlastni.Select(k => k.Nazev));
|
||
Assert.Equal(new[] { "Karlovarský" }, rozdil.Skryte);
|
||
|
||
// A po restartu programu tam ta úprava musí být.
|
||
var poRestartu = new ObservableCollection<Kraj>();
|
||
CiselnikyStore.Sluc(poRestartu, dodavana, rozdil);
|
||
Assert.Equal(new[] { "Jihomoravský", "Karlovarskýýýýý" }, poRestartu.Select(k => k.Nazev));
|
||
}
|
||
|
||
[Fact]
|
||
public void UpravaNemutujeDodavanouVrstvu()
|
||
{
|
||
var dodavana = new List<Organizace> { new() { Nazev = "SŽ Praha", Adresa = "Dlážděná 1003/7" } };
|
||
|
||
var vMrizce = new ObservableCollection<Organizace>();
|
||
CiselnikyStore.Sluc(vMrizce, dodavana, null);
|
||
vMrizce[0].Adresa = "jiná adresa";
|
||
|
||
Assert.Equal("Dlážděná 1003/7", dodavana[0].Adresa);
|
||
Assert.NotSame(dodavana[0], vMrizce[0]);
|
||
}
|
||
|
||
[Fact]
|
||
public void KlicSeBerePodleTypuPolozky()
|
||
{
|
||
Assert.Equal("SŽ", CiselnikyStore.Klic(new Organizace { Nazev = "SŽ" }));
|
||
Assert.Equal("EXprojekt", CiselnikyStore.Klic(new Firma { Nazev = "EXprojekt" }));
|
||
Assert.Equal("Novák", CiselnikyStore.Klic(new Jmeno { Hodnota = "Novák" }));
|
||
Assert.Equal("DPS", CiselnikyStore.Klic(new VerzeDokumentace { Hodnota = "DPS" }));
|
||
Assert.Equal("D", CiselnikyStore.Klic(new Cast { Oznaceni = "D", Nazev = "Mosty" }));
|
||
Assert.Equal("Jihomoravský", CiselnikyStore.Klic(new Kraj { Nazev = "Jihomoravský" }));
|
||
}
|
||
}
|