1+ #! /bin/bash
2+
3+ set -euo pipefail
4+
5+ source " $( cd " $( dirname " ${BASH_SOURCE[0]} " ) " && pwd) /lib.sh"
6+ src_dir=${src_dir:? }
7+ workdir=${workdir:? }
8+
9+ assert_json_array () {
10+ [ " $( jq -r ' type' " $1 " ) " = " array" ] || fail " Expected array root in $1 "
11+ }
12+
13+ assert_json_length () {
14+ local actual
15+ actual=$( jq ' length' " $1 " )
16+ [ " $actual " -eq " $2 " ] || fail " Expected $1 to contain $2 items, got $actual "
17+ }
18+
19+ assert_repo_only () {
20+ jq -e --arg repo " $2 " ' all(.[]; .repo == $repo)' " $1 " > /dev/null || fail " Expected $1 to contain only repo $2 "
21+ }
22+
23+ assert_size_lt () {
24+ local size
25+ size=$( stat -c %s " $1 " )
26+ [ " $size " -lt " $2 " ] || fail " Expected $1 to be smaller than $2 bytes, got $size "
27+ }
28+
29+ assert_size_gt () {
30+ local size
31+ size=$( stat -c %s " $1 " )
32+ [ " $size " -gt " $2 " ] || fail " Expected $1 to be larger than $2 bytes, got $size "
33+ }
34+
35+ version_total () {
36+ jq ' [ .[] | ((.version // []) | length) ] | add' " $1 "
37+ }
38+
39+ write_package_json () {
40+ local file=$1
41+ local owner=$2
42+ local repo=$3
43+ local package=$4
44+ local version_count=$5
45+ local payload_file=$6
46+
47+ jq -nc \
48+ --arg owner " $owner " \
49+ --arg repo " $repo " \
50+ --arg package " $package " \
51+ --arg date " 2026-03-30" \
52+ --argjson version_count " $version_count " \
53+ --rawfile payload " $payload_file " '
54+ {
55+ owner: $owner,
56+ repo: $repo,
57+ package: $package,
58+ downloads: "1",
59+ raw_downloads: 1,
60+ date: $date,
61+ version: [
62+ range(0; $version_count) | {
63+ id: (100000 + .),
64+ name: ("v" + (. | tostring)),
65+ tags: [if . == ($version_count - 1) then "latest" else ("tag-" + (. | tostring)) end],
66+ downloads: "1",
67+ raw_downloads: 1,
68+ date: $date,
69+ notes: $payload
70+ }
71+ ]
72+ }' > " $file "
73+ }
74+
75+ build_owner_arrays () {
76+ local owner_dir=$1
77+ local repo
78+
79+ find " $owner_dir " -type f -name ' *.json' ! -name ' .*' -print0 | xargs -0 jq -cs ' .' > " $owner_dir /.json.tmp"
80+ mv -f " $owner_dir /.json.tmp" " $owner_dir /.json"
81+ bash " $src_dir /lib/ytoxt.sh" " $owner_dir /.json" > /dev/null
82+
83+ while IFS= read -r repo; do
84+ [ -n " $repo " ] || continue
85+ jq -c --arg repo " $repo " ' [.[] | select(.repo == $repo)]' " $owner_dir /.json" > " $owner_dir /$repo /.json.tmp"
86+ mv -f " $owner_dir /$repo /.json.tmp" " $owner_dir /$repo /.json"
87+ bash " $src_dir /lib/ytoxt.sh" " $owner_dir /$repo /.json" > /dev/null
88+ done < <( find " $owner_dir " -mindepth 1 -maxdepth 1 -type d -printf ' %f\n' | sort)
89+ }
90+
91+ test_small_owner_and_repo_arrays () {
92+ local owner_dir=" $workdir /small/Lazztech"
93+ local empty_payload=" $workdir /empty.txt"
94+
95+ : > " $empty_payload "
96+ mkdir -p " $owner_dir /Libre-Closet" " $owner_dir /SideRepo"
97+
98+ write_package_json " $owner_dir /Libre-Closet/libre-closet.json" " Lazztech" " Libre-Closet" " libre-closet" 3 " $empty_payload "
99+ write_package_json " $owner_dir /Libre-Closet/libre-closet-dev.json" " Lazztech" " Libre-Closet" " libre-closet-dev" 2 " $empty_payload "
100+ write_package_json " $owner_dir /SideRepo/sidecar.json" " Lazztech" " SideRepo" " sidecar" 1 " $empty_payload "
101+
102+ build_owner_arrays " $owner_dir "
103+
104+ assert_file_exists " $owner_dir /.json"
105+ assert_file_exists " $owner_dir /.xml"
106+ assert_json_array " $owner_dir /.json"
107+ assert_json_length " $owner_dir /.json" 3
108+ assert_size_lt " $owner_dir /.json" 50000000
109+ assert_size_lt " $owner_dir /.xml" 50000000
110+ assert_contains " $owner_dir /.xml" " libre-closet"
111+ assert_contains " $owner_dir /.xml" " sidecar"
112+
113+ assert_file_exists " $owner_dir /Libre-Closet/.json"
114+ assert_file_exists " $owner_dir /Libre-Closet/.xml"
115+ assert_json_array " $owner_dir /Libre-Closet/.json"
116+ assert_json_length " $owner_dir /Libre-Closet/.json" 2
117+ assert_repo_only " $owner_dir /Libre-Closet/.json" " Libre-Closet"
118+ assert_contains " $owner_dir /Libre-Closet/.xml" " libre-closet"
119+ assert_not_contains " $owner_dir /Libre-Closet/.xml" " sidecar"
120+
121+ assert_file_exists " $owner_dir /SideRepo/.json"
122+ assert_file_exists " $owner_dir /SideRepo/.xml"
123+ assert_json_array " $owner_dir /SideRepo/.json"
124+ assert_json_length " $owner_dir /SideRepo/.json" 1
125+ assert_repo_only " $owner_dir /SideRepo/.json" " SideRepo"
126+ assert_contains " $owner_dir /SideRepo/.xml" " sidecar"
127+ assert_not_contains " $owner_dir /SideRepo/.xml" " libre-closet"
128+ }
129+
130+ test_large_array_trimming () {
131+ local payload_file=" $workdir /payload.txt"
132+ local empty_payload=" $workdir /empty-large.txt"
133+ local base_dir=" $workdir /large"
134+ local owner_array=" $base_dir /owner-large.json"
135+ local repo_array=" $base_dir /repo-large.json"
136+ local owner_versions_before
137+ local owner_versions_after
138+ local repo_versions_before
139+ local repo_versions_after
140+
141+ : > " $empty_payload "
142+ mkdir -p " $base_dir "
143+ head -c 700000 /dev/zero | tr ' \0' ' a' > " $payload_file "
144+
145+ write_package_json " $base_dir /libre-closet.json" " Lazztech" " Libre-Closet" " libre-closet" 75 " $payload_file "
146+ write_package_json " $base_dir /sidecar.json" " Lazztech" " SideRepo" " sidecar" 1 " $empty_payload "
147+
148+ jq -cs ' .' " $base_dir /libre-closet.json" " $base_dir /sidecar.json" > " $owner_array "
149+ jq -c ' [.[] | select(.repo == "Libre-Closet")]' " $owner_array " > " $repo_array "
150+
151+ assert_json_array " $owner_array "
152+ assert_json_array " $repo_array "
153+ assert_size_gt " $owner_array " 50000000
154+ assert_size_gt " $repo_array " 50000000
155+
156+ owner_versions_before=$( version_total " $owner_array " )
157+ repo_versions_before=$( version_total " $repo_array " )
158+
159+ bash " $src_dir /lib/ytoxt.sh" " $owner_array " > /dev/null
160+ bash " $src_dir /lib/ytoxt.sh" " $repo_array " > /dev/null
161+
162+ assert_file_exists " ${owner_array% .* } .xml"
163+ assert_file_exists " ${repo_array% .* } .xml"
164+ assert_json_array " $owner_array "
165+ assert_json_array " $repo_array "
166+ assert_size_lt " $owner_array " 50000000
167+ assert_size_lt " ${owner_array% .* } .xml" 50000000
168+ assert_size_lt " $repo_array " 50000000
169+ assert_size_lt " ${repo_array% .* } .xml" 50000000
170+ assert_contains " ${owner_array% .* } .xml" " libre-closet"
171+ assert_contains " ${repo_array% .* } .xml" " libre-closet"
172+ assert_repo_only " $repo_array " " Libre-Closet"
173+
174+ owner_versions_after=$( version_total " $owner_array " )
175+ repo_versions_after=$( version_total " $repo_array " )
176+ [ " $owner_versions_after " -lt " $owner_versions_before " ] || fail " Expected owner array trimming to remove versions"
177+ [ " $repo_versions_after " -lt " $repo_versions_before " ] || fail " Expected repo array trimming to remove versions"
178+ }
179+
180+ trap cleanup EXIT
181+
182+ test_small_owner_and_repo_arrays
183+ test_large_array_trimming
184+
185+ echo " Array creation regression tests passed"
0 commit comments