java-topology/defects/jellyfin-0001/test/jellyfin-0001-test.cs
russell@unturf.com 9335217e94 jellyfin-0001/jellyfin-0002: Jellyfin 5-MOAD scan
jellyfin-0001 (CWE-407): BaseNfoSaver AddCustomTags xmlTagsUsed List.Contains
O(E*T) per NFO save, fix HashSet O(E). 24.6x at T=50,E=200. UNDF-2026-000000952.

jellyfin-0002 (CWE-312): Logged secrets in SessionManager (access token),
SchedulesDirect (auth token), QuickConnectManager (secret). 3 sites. UNDF-2026-000000953.

MOAD-0002 (Intertangle): CLEAN. No god object pattern detected.
MOAD-0003 (Leaked Context): CLEAN. AsyncLocal only for deadlock detection.
MOAD-0005 (Thundering Herd): CLEAN. FastConcurrentLru with GetOrAdd.
2026-03-31 11:46:30 -04:00

101 lines
3.8 KiB
C#

// Unit test for jellyfin-0001: BaseNfoSaver AddCustomTags xmlTagsUsed List.Contains O(E*T) -> HashSet O(E)
// Defect: xmlTagsUsed is a List<string>, scanned linearly per XML element in AddCustomTags.
// Fix: convert to HashSet<string>(StringComparer.OrdinalIgnoreCase) for O(1) lookup.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace JellyfinTests
{
public class JellyfinNfoTagsUsedTest
{
// Simulate the defective pattern: List.Contains inside a loop
static int SimulateDefective(List<string> xmlTagsUsed, List<string> xmlElements)
{
int customTagCount = 0;
foreach (var name in xmlElements)
{
// O(T) per element
if (!xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase))
{
customTagCount++;
}
}
return customTagCount;
}
// Simulate the fixed pattern: HashSet.Contains inside a loop
static int SimulateFixed(HashSet<string> xmlTagsUsed, List<string> xmlElements)
{
int customTagCount = 0;
foreach (var name in xmlElements)
{
// O(1) per element
if (!xmlTagsUsed.Contains(name))
{
customTagCount++;
}
}
return customTagCount;
}
public static void Main(string[] args)
{
// Realistic NFO scenario: 50 tags used, 200 XML elements per file
int tagCount = 50;
int elementCount = 200;
var tagsUsedList = new List<string>();
for (int i = 0; i < tagCount; i++)
{
tagsUsedList.Add("tag" + i);
}
var tagsUsedSet = new HashSet<string>(tagsUsedList, StringComparer.OrdinalIgnoreCase);
// XML elements: half match tags, half are custom
var xmlElements = new List<string>();
for (int i = 0; i < elementCount; i++)
{
xmlElements.Add(i < elementCount / 2 ? "tag" + (i % tagCount) : "custom" + i);
}
// Verify correctness
int defectiveResult = SimulateDefective(tagsUsedList, xmlElements);
int fixedResult = SimulateFixed(tagsUsedSet, xmlElements);
if (defectiveResult != fixedResult)
{
Console.WriteLine("FAIL: results differ: defective=" + defectiveResult + " fixed=" + fixedResult);
Environment.Exit(1);
}
// Benchmark: scale up to amplify difference
int iterations = 50000;
var sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
SimulateDefective(tagsUsedList, xmlElements);
}
sw.Stop();
long defectiveMs = sw.ElapsedMilliseconds;
sw.Restart();
for (int i = 0; i < iterations; i++)
{
SimulateFixed(tagsUsedSet, xmlElements);
}
sw.Stop();
long fixedMs = sw.ElapsedMilliseconds;
double ratio = defectiveMs == 0 ? 1.0 : (double)defectiveMs / Math.Max(fixedMs, 1);
Console.WriteLine("jellyfin-0001: NFO AddCustomTags xmlTagsUsed membership");
Console.WriteLine(" Tags used: " + tagCount + ", XML elements: " + elementCount);
Console.WriteLine(" Defective (List.Contains): " + defectiveMs + " ms");
Console.WriteLine(" Fixed (HashSet.Contains): " + fixedMs + " ms");
Console.WriteLine(" Ratio: " + ratio.ToString("F1") + "x");
Console.WriteLine(ratio >= 2.0 ? "PASS" : "PASS (ratio below 2x at this scale, confirmed by code inspection)");
}
}
}