web_test.v

4.0 KB · raw

1//! The web backend: public repos over HTTP, and never a write.23module remote45import net6import net.http78struct FakeMirror {9mut:10	asked []string11}1213fn (mut h FakeMirror) handle(req http.Request) http.Response {14	h.asked << req.url15	if req.url == '/repo/koh/format' {16		return http.Response{ status_code: 200, status_msg: 'OK', body: 'koh-remote 1' }17	}18	if req.url == '/repo/koh/head' {19		return http.Response{ status_code: 200, status_msg: 'OK', body: 'abc123abc123' }20	}21	if req.url == '/repo/koh/saves/abc123abc123' {22		return http.Response{ status_code: 200, status_msg: 'OK', body: '{"id":"abc123abc123","files":[]}' }23	}24	if req.url == '/repo/koh/objects/dead' {25		// Raw bytes, not text: an object is zlib, and must survive intact.26		return http.Response{ status_code: 200, status_msg: 'OK', body: '\x01\x02\x03' }27	}28	if req.url == '/repo/koh/events' {29		return http.Response{ status_code: 200, status_msg: 'OK', body: 'e1\ne2\n' }30	}31	if req.url == '/repo/koh/broken' {32		return http.Response{ status_code: 500, status_msg: 'Internal Server Error' }33	}34	return http.Response{ status_code: 404, status_msg: 'Not Found' }35}3637fn start_fake_mirror() !(int, &http.Server, &FakeMirror) {38	for port in 19321 .. 19331 {39		listener := net.listen_tcp(.ip, '127.0.0.1:${port}') or { continue }40		fake := &FakeMirror{}41		mut server := &http.Server{42			listener:             listener43			handler:              fake44			show_startup_message: false45		}46		spawn server.listen_and_serve()47		server.wait_till_running()!48		return port, server, fake49	}50	return error('no free fake mirror port')51}5253fn test_a_public_repo_reads_through_the_web_backend() {54	port, mut server, _ := start_fake_mirror()!55	defer {56		server.stop()57	}58	mut web := new_web('http://127.0.0.1:${port}')!59	format := web.get('repo/format')!60	assert format.found61	assert format.body.bytestr() == 'koh-remote 1'62	assert web.get('repo/head')!.body.bytestr() == 'abc123abc123'63	assert web.get('repo/saves/abc123abc123')!.body.bytestr() == '{"id":"abc123abc123","files":[]}'64	// Objects travel as stored, byte for byte: the client inflates them itself.65	blob := web.get('repo/objects/dead')!66	assert blob.found67	assert blob.body == [u8(1), 2, 3]68	// The event list becomes the keys `koh steal` expects.69	assert web.list('repo/events')! == ['repo/events/e1', 'repo/events/e2']70	// A missing key is an answer, not a failure.71	assert !web.get('repo/objects/nope')!.found72	assert web.list('repo/events/none')! == []string{}73}7475fn test_a_broken_mirror_and_every_write_are_refused() {76	port, mut server, _ := start_fake_mirror()!77	defer {78		server.stop()79	}80	mut web := new_web('http://127.0.0.1:${port}')!81	if _ := web.get('repo/broken') {82		assert false, 'a 500 must not read as a missing key'83	} else {84		assert err.msg().contains('500')85	}86	read_only := 'http://127.0.0.1:${port}/repo is read-only.'87	if _ := web.put_if_absent('repo/head', 'x'.bytes()) {88		assert false, 'a public repo is never written to'89	} else {90		assert err.msg() == read_only91	}92	if _ := web.put_if_match('repo/head', 'x'.bytes(), '"tag"') {93		assert false, 'a public repo is never written to'94	} else {95		assert err.msg() == read_only96	}97	if _ := web.delete('repo/head') {98		assert false, 'a public repo is never written to'99	} else {100		assert err.msg() == read_only101	}102}103104fn test_only_a_bare_host_becomes_a_base() {105	assert kepr_host('kepr.uk')! == 'https://kepr.uk'106	assert kepr_host('https://kepr.uk/')! == 'https://kepr.uk'107	assert kepr_host('http://127.0.0.1:3917')! == 'http://127.0.0.1:3917'108	for bad in ['kepr.uk/foo', 'kepr.uk?x=1', '[email protected]', 'kepr uk', 'https://'] {109		if _ := kepr_host(bad) {110			assert false, '${bad} is not a host'111		}112	}113}114115// The base is normalised once, so a call never builds a URL from what was typed.116fn test_the_base_is_normalised_once() {117	port, mut server, fake := start_fake_mirror()!118	defer {119		server.stop()120	}121	mut web := new_web('http://127.0.0.1:${port}/')!122	assert web.base == 'http://127.0.0.1:${port}'123	assert web.get('repo/head')!.found124	assert fake.asked[0] == '/repo/koh/head'125}