Files
Rozpisky/Rozpisky.Tests/CiselnikyVrstvyTesty.cs
marekandClaude Opus 5 f086e623d6 Oprava: úprava dodávané položky číselníku se tiše ztrácela
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>
2026-09-07 11:26:10 +02:00

235 lines
9.0 KiB
C#
Raw Permalink 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.
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ý" }));
}
}