xref: /freebsd/libexec/pkg-serve/tests/pkg_serve_test.sh (revision b42e852e89cb04cceb6e0226d6a08cab13fb6e90)
1*b42e852eSBaptiste Daroussin#-
2*b42e852eSBaptiste Daroussin# SPDX-License-Identifier: BSD-2-Clause
3*b42e852eSBaptiste Daroussin#
4*b42e852eSBaptiste Daroussin# Copyright (c) 2026 Baptiste Daroussin <bapt@FreeBSD.org>
5*b42e852eSBaptiste Daroussin
6*b42e852eSBaptiste DaroussinPKG_SERVE="${PKG_SERVE:-/usr/libexec/pkg-serve}"
7*b42e852eSBaptiste Daroussin
8*b42e852eSBaptiste Daroussinserve()
9*b42e852eSBaptiste Daroussin{
10*b42e852eSBaptiste Daroussin	printf "$1" | "${PKG_SERVE}" "$2"
11*b42e852eSBaptiste Daroussin}
12*b42e852eSBaptiste Daroussin
13*b42e852eSBaptiste Daroussincheck_output()
14*b42e852eSBaptiste Daroussin{
15*b42e852eSBaptiste Daroussin	local pattern="$1" ; shift
16*b42e852eSBaptiste Daroussin	output=$(serve "$@")
17*b42e852eSBaptiste Daroussin	case "$output" in
18*b42e852eSBaptiste Daroussin	*${pattern}*)
19*b42e852eSBaptiste Daroussin		return 0
20*b42e852eSBaptiste Daroussin		;;
21*b42e852eSBaptiste Daroussin	*)
22*b42e852eSBaptiste Daroussin		echo "Expected pattern: ${pattern}"
23*b42e852eSBaptiste Daroussin		echo "Got: ${output}"
24*b42e852eSBaptiste Daroussin		return 1
25*b42e852eSBaptiste Daroussin		;;
26*b42e852eSBaptiste Daroussin	esac
27*b42e852eSBaptiste Daroussin}
28*b42e852eSBaptiste Daroussin
29*b42e852eSBaptiste Daroussinatf_test_case greeting
30*b42e852eSBaptiste Daroussingreeting_head()
31*b42e852eSBaptiste Daroussin{
32*b42e852eSBaptiste Daroussin	atf_set "descr" "Server sends greeting on connect"
33*b42e852eSBaptiste Daroussin}
34*b42e852eSBaptiste Daroussingreeting_body()
35*b42e852eSBaptiste Daroussin{
36*b42e852eSBaptiste Daroussin	mkdir repo
37*b42e852eSBaptiste Daroussin	check_output "ok: pkg-serve " "quit\n" repo ||
38*b42e852eSBaptiste Daroussin	    atf_fail "greeting not found"
39*b42e852eSBaptiste Daroussin}
40*b42e852eSBaptiste Daroussin
41*b42e852eSBaptiste Daroussinatf_test_case unknown_command
42*b42e852eSBaptiste Daroussinunknown_command_head()
43*b42e852eSBaptiste Daroussin{
44*b42e852eSBaptiste Daroussin	atf_set "descr" "Unknown commands get ko response"
45*b42e852eSBaptiste Daroussin}
46*b42e852eSBaptiste Daroussinunknown_command_body()
47*b42e852eSBaptiste Daroussin{
48*b42e852eSBaptiste Daroussin	mkdir repo
49*b42e852eSBaptiste Daroussin	check_output "ko: unknown command 'plop'" "plop\nquit\n" repo ||
50*b42e852eSBaptiste Daroussin	    atf_fail "expected ko for unknown command"
51*b42e852eSBaptiste Daroussin}
52*b42e852eSBaptiste Daroussin
53*b42e852eSBaptiste Daroussinatf_test_case get_missing_file
54*b42e852eSBaptiste Daroussinget_missing_file_head()
55*b42e852eSBaptiste Daroussin{
56*b42e852eSBaptiste Daroussin	atf_set "descr" "Requesting a missing file returns ko"
57*b42e852eSBaptiste Daroussin}
58*b42e852eSBaptiste Daroussinget_missing_file_body()
59*b42e852eSBaptiste Daroussin{
60*b42e852eSBaptiste Daroussin	mkdir repo
61*b42e852eSBaptiste Daroussin	check_output "ko: file not found" "get nonexistent.pkg 0\nquit\n" repo ||
62*b42e852eSBaptiste Daroussin	    atf_fail "expected file not found"
63*b42e852eSBaptiste Daroussin}
64*b42e852eSBaptiste Daroussin
65*b42e852eSBaptiste Daroussinatf_test_case get_file
66*b42e852eSBaptiste Daroussinget_file_head()
67*b42e852eSBaptiste Daroussin{
68*b42e852eSBaptiste Daroussin	atf_set "descr" "Requesting an existing file returns its content"
69*b42e852eSBaptiste Daroussin}
70*b42e852eSBaptiste Daroussinget_file_body()
71*b42e852eSBaptiste Daroussin{
72*b42e852eSBaptiste Daroussin	mkdir repo
73*b42e852eSBaptiste Daroussin	echo "testcontent" > repo/test.pkg
74*b42e852eSBaptiste Daroussin	output=$(serve "get test.pkg 0\nquit\n" repo)
75*b42e852eSBaptiste Daroussin	echo "$output" | grep -q "ok: 12" ||
76*b42e852eSBaptiste Daroussin	    atf_fail "expected ok: 12, got: ${output}"
77*b42e852eSBaptiste Daroussin	echo "$output" | grep -q "testcontent" ||
78*b42e852eSBaptiste Daroussin	    atf_fail "expected testcontent in output"
79*b42e852eSBaptiste Daroussin}
80*b42e852eSBaptiste Daroussin
81*b42e852eSBaptiste Daroussinatf_test_case get_file_leading_slash
82*b42e852eSBaptiste Daroussinget_file_leading_slash_head()
83*b42e852eSBaptiste Daroussin{
84*b42e852eSBaptiste Daroussin	atf_set "descr" "Leading slash in path is stripped"
85*b42e852eSBaptiste Daroussin}
86*b42e852eSBaptiste Daroussinget_file_leading_slash_body()
87*b42e852eSBaptiste Daroussin{
88*b42e852eSBaptiste Daroussin	mkdir repo
89*b42e852eSBaptiste Daroussin	echo "testcontent" > repo/test.pkg
90*b42e852eSBaptiste Daroussin	check_output "ok: 12" "get /test.pkg 0\nquit\n" repo ||
91*b42e852eSBaptiste Daroussin	    atf_fail "leading slash not stripped"
92*b42e852eSBaptiste Daroussin}
93*b42e852eSBaptiste Daroussin
94*b42e852eSBaptiste Daroussinatf_test_case get_file_uptodate
95*b42e852eSBaptiste Daroussinget_file_uptodate_head()
96*b42e852eSBaptiste Daroussin{
97*b42e852eSBaptiste Daroussin	atf_set "descr" "File with old mtime returns ok: 0"
98*b42e852eSBaptiste Daroussin}
99*b42e852eSBaptiste Daroussinget_file_uptodate_body()
100*b42e852eSBaptiste Daroussin{
101*b42e852eSBaptiste Daroussin	mkdir repo
102*b42e852eSBaptiste Daroussin	echo "testcontent" > repo/test.pkg
103*b42e852eSBaptiste Daroussin	check_output "ok: 0" "get test.pkg 9999999999\nquit\n" repo ||
104*b42e852eSBaptiste Daroussin	    atf_fail "expected ok: 0 for up-to-date file"
105*b42e852eSBaptiste Daroussin}
106*b42e852eSBaptiste Daroussin
107*b42e852eSBaptiste Daroussinatf_test_case get_directory
108*b42e852eSBaptiste Daroussinget_directory_head()
109*b42e852eSBaptiste Daroussin{
110*b42e852eSBaptiste Daroussin	atf_set "descr" "Requesting a directory returns ko"
111*b42e852eSBaptiste Daroussin}
112*b42e852eSBaptiste Daroussinget_directory_body()
113*b42e852eSBaptiste Daroussin{
114*b42e852eSBaptiste Daroussin	mkdir -p repo/subdir
115*b42e852eSBaptiste Daroussin	check_output "ko: not a file" "get subdir 0\nquit\n" repo ||
116*b42e852eSBaptiste Daroussin	    atf_fail "expected not a file"
117*b42e852eSBaptiste Daroussin}
118*b42e852eSBaptiste Daroussin
119*b42e852eSBaptiste Daroussinatf_test_case get_missing_age
120*b42e852eSBaptiste Daroussinget_missing_age_head()
121*b42e852eSBaptiste Daroussin{
122*b42e852eSBaptiste Daroussin	atf_set "descr" "get without age argument returns error"
123*b42e852eSBaptiste Daroussin}
124*b42e852eSBaptiste Daroussinget_missing_age_body()
125*b42e852eSBaptiste Daroussin{
126*b42e852eSBaptiste Daroussin	mkdir repo
127*b42e852eSBaptiste Daroussin	check_output "ko: bad command get" "get test.pkg\nquit\n" repo ||
128*b42e852eSBaptiste Daroussin	    atf_fail "expected bad command get"
129*b42e852eSBaptiste Daroussin}
130*b42e852eSBaptiste Daroussin
131*b42e852eSBaptiste Daroussinatf_test_case get_bad_age
132*b42e852eSBaptiste Daroussinget_bad_age_head()
133*b42e852eSBaptiste Daroussin{
134*b42e852eSBaptiste Daroussin	atf_set "descr" "get with non-numeric age returns error"
135*b42e852eSBaptiste Daroussin}
136*b42e852eSBaptiste Daroussinget_bad_age_body()
137*b42e852eSBaptiste Daroussin{
138*b42e852eSBaptiste Daroussin	mkdir repo
139*b42e852eSBaptiste Daroussin	check_output "ko: bad number" "get test.pkg notanumber\nquit\n" repo ||
140*b42e852eSBaptiste Daroussin	    atf_fail "expected bad number"
141*b42e852eSBaptiste Daroussin}
142*b42e852eSBaptiste Daroussin
143*b42e852eSBaptiste Daroussinatf_test_case get_empty_arg
144*b42e852eSBaptiste Daroussinget_empty_arg_head()
145*b42e852eSBaptiste Daroussin{
146*b42e852eSBaptiste Daroussin	atf_set "descr" "get with no arguments returns error"
147*b42e852eSBaptiste Daroussin}
148*b42e852eSBaptiste Daroussinget_empty_arg_body()
149*b42e852eSBaptiste Daroussin{
150*b42e852eSBaptiste Daroussin	mkdir repo
151*b42e852eSBaptiste Daroussin	check_output "ko: bad command get" "get \nquit\n" repo ||
152*b42e852eSBaptiste Daroussin	    atf_fail "expected bad command get"
153*b42e852eSBaptiste Daroussin}
154*b42e852eSBaptiste Daroussin
155*b42e852eSBaptiste Daroussinatf_test_case path_traversal
156*b42e852eSBaptiste Daroussinpath_traversal_head()
157*b42e852eSBaptiste Daroussin{
158*b42e852eSBaptiste Daroussin	atf_set "descr" "Path traversal with .. is rejected"
159*b42e852eSBaptiste Daroussin}
160*b42e852eSBaptiste Daroussinpath_traversal_body()
161*b42e852eSBaptiste Daroussin{
162*b42e852eSBaptiste Daroussin	mkdir repo
163*b42e852eSBaptiste Daroussin	check_output "ko: file not found" \
164*b42e852eSBaptiste Daroussin	    "get ../etc/passwd 0\nquit\n" repo ||
165*b42e852eSBaptiste Daroussin	    atf_fail "path traversal not rejected"
166*b42e852eSBaptiste Daroussin}
167*b42e852eSBaptiste Daroussin
168*b42e852eSBaptiste Daroussinatf_test_case get_subdir_file
169*b42e852eSBaptiste Daroussinget_subdir_file_head()
170*b42e852eSBaptiste Daroussin{
171*b42e852eSBaptiste Daroussin	atf_set "descr" "Files in subdirectories are served"
172*b42e852eSBaptiste Daroussin}
173*b42e852eSBaptiste Daroussinget_subdir_file_body()
174*b42e852eSBaptiste Daroussin{
175*b42e852eSBaptiste Daroussin	mkdir -p repo/sub
176*b42e852eSBaptiste Daroussin	echo "subcontent" > repo/sub/file.pkg
177*b42e852eSBaptiste Daroussin	output=$(serve "get sub/file.pkg 0\nquit\n" repo)
178*b42e852eSBaptiste Daroussin	echo "$output" | grep -q "ok: 11" ||
179*b42e852eSBaptiste Daroussin	    atf_fail "expected ok: 11, got: ${output}"
180*b42e852eSBaptiste Daroussin	echo "$output" | grep -q "subcontent" ||
181*b42e852eSBaptiste Daroussin	    atf_fail "expected subcontent in output"
182*b42e852eSBaptiste Daroussin}
183*b42e852eSBaptiste Daroussin
184*b42e852eSBaptiste Daroussinatf_test_case multiple_gets
185*b42e852eSBaptiste Daroussinmultiple_gets_head()
186*b42e852eSBaptiste Daroussin{
187*b42e852eSBaptiste Daroussin	atf_set "descr" "Multiple get commands in one session"
188*b42e852eSBaptiste Daroussin}
189*b42e852eSBaptiste Daroussinmultiple_gets_body()
190*b42e852eSBaptiste Daroussin{
191*b42e852eSBaptiste Daroussin	mkdir repo
192*b42e852eSBaptiste Daroussin	echo "aaa" > repo/a.pkg
193*b42e852eSBaptiste Daroussin	echo "bbb" > repo/b.pkg
194*b42e852eSBaptiste Daroussin	output=$(serve "get a.pkg 0\nget b.pkg 0\nquit\n" repo)
195*b42e852eSBaptiste Daroussin	echo "$output" | grep -q "ok: 4" ||
196*b42e852eSBaptiste Daroussin	    atf_fail "expected ok: 4 for a.pkg"
197*b42e852eSBaptiste Daroussin	echo "$output" | grep -q "aaa" ||
198*b42e852eSBaptiste Daroussin	    atf_fail "expected content of a.pkg"
199*b42e852eSBaptiste Daroussin	echo "$output" | grep -q "bbb" ||
200*b42e852eSBaptiste Daroussin	    atf_fail "expected content of b.pkg"
201*b42e852eSBaptiste Daroussin}
202*b42e852eSBaptiste Daroussin
203*b42e852eSBaptiste Daroussinatf_test_case bad_basedir
204*b42e852eSBaptiste Daroussinbad_basedir_head()
205*b42e852eSBaptiste Daroussin{
206*b42e852eSBaptiste Daroussin	atf_set "descr" "Non-existent basedir causes exit failure"
207*b42e852eSBaptiste Daroussin}
208*b42e852eSBaptiste Daroussinbad_basedir_body()
209*b42e852eSBaptiste Daroussin{
210*b42e852eSBaptiste Daroussin	atf_check -s not-exit:0 -e match:"open" \
211*b42e852eSBaptiste Daroussin	    "${PKG_SERVE}" /nonexistent/path
212*b42e852eSBaptiste Daroussin}
213*b42e852eSBaptiste Daroussin
214*b42e852eSBaptiste Daroussinatf_init_test_cases()
215*b42e852eSBaptiste Daroussin{
216*b42e852eSBaptiste Daroussin	atf_add_test_case greeting
217*b42e852eSBaptiste Daroussin	atf_add_test_case unknown_command
218*b42e852eSBaptiste Daroussin	atf_add_test_case get_missing_file
219*b42e852eSBaptiste Daroussin	atf_add_test_case get_file
220*b42e852eSBaptiste Daroussin	atf_add_test_case get_file_leading_slash
221*b42e852eSBaptiste Daroussin	atf_add_test_case get_file_uptodate
222*b42e852eSBaptiste Daroussin	atf_add_test_case get_directory
223*b42e852eSBaptiste Daroussin	atf_add_test_case get_missing_age
224*b42e852eSBaptiste Daroussin	atf_add_test_case get_bad_age
225*b42e852eSBaptiste Daroussin	atf_add_test_case get_empty_arg
226*b42e852eSBaptiste Daroussin	atf_add_test_case path_traversal
227*b42e852eSBaptiste Daroussin	atf_add_test_case get_subdir_file
228*b42e852eSBaptiste Daroussin	atf_add_test_case multiple_gets
229*b42e852eSBaptiste Daroussin	atf_add_test_case bad_basedir
230*b42e852eSBaptiste Daroussin}
231