library_check_test.v

4.6 KB · raw

1//! Phase 0 library checks.2//!3//! Koh 3 depends on five things outside its own source: SQLite (verified in4//! src/storage), Blake3, zlib, SHA-256/HMAC and an HTTP client. This file pins5//! the behaviour Koh relies on for the four stdlib ones, against vectors6//! produced by other implementations of the same algorithms. If a V upgrade7//! breaks an assumption, it breaks here rather than in a save.89module tests1011import compress.zlib12import crypto.blake313import crypto.hmac14import crypto.sha25615import net16import net.http17import time1819// Koh 3 is built and tested on V master at b9a2d87, and that revision is the pin:20// V master moves, and a newer V may break Koh at any time. Building with any21// other compiler fails here rather than somewhere stranger.22fn test_built_with_the_pinned_v() {23	assert @VCURRENTHASH == 'b9a2d87'24}2526// Vectors from the official blake3 bindings, cross-checked against the27// naming of real Koh 2 objects: `.koh/objects/<hash[0..2]>/<hash[2..]>` is28// blake3(uncompressed bytes) of a zlib stream.29fn test_blake3_vectors() {30	assert blake3.sum256(''.bytes()).hex() == 'af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262'31	assert blake3.sum256('abc'.bytes()).hex() == '6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85'32	assert blake3.sum256('The quick brown fox jumps over the lazy dog'.bytes()).hex() == '2f1514181aadccd913abd94cfa592701a5686ab23f8df1dff1b74710febc6d4a'33}3435fn test_blake3_streaming_matches_one_shot() {36	mut d := blake3.Digest.new_hash()!37	d.write('The quick brown '.bytes())!38	d.write('fox jumps over the lazy dog'.bytes())!39	assert d.checksum(32) == blake3.sum256('The quick brown fox jumps over the lazy dog'.bytes())40}4142fn test_zlib_round_trip() {43	// Object bytes must survive a trip through the store byte for byte,44	// including an empty payload and one with every byte value.45	mut all_bytes := []u8{len: 256}46	for i in 0 .. 256 {47		all_bytes[i] = u8(i)48	}49	mut payloads := [[]u8{}, 'hello koh'.bytes()]50	payloads << all_bytes51	for payload in payloads {52		packed := zlib.compress(payload)!53		assert zlib.decompress(packed)! == payload54	}55}5657fn test_sha256_and_hmac_vectors() {58	assert sha256.sum256('abc'.bytes()).hex() == 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'59	assert sha256.hexhash('abc') == 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'60	// HMAC-SHA256, the primitive behind SigV4 signing.61	mac := hmac.new('key'.bytes(), 'The quick brown fox jumps over the lazy dog'.bytes(),62		sha256.sum256, 64)63	assert mac.hex() == 'f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8'64}6566struct EtagHandler {67mut:68	seen_if_none_match string69}7071fn (mut h EtagHandler) handle(req http.Request) http.Response {72	h.seen_if_none_match = req.header.get_custom('If-None-Match', http.HeaderQueryConfig{}) or {73		''74	}75	mut resp := http.Response{76		body: 'payload'77	}78	if h.seen_if_none_match == '*' {79		resp.status_code = 41280		resp.status_msg = 'Precondition Failed'81	} else {82		resp.status_code = 20083		resp.status_msg = 'OK'84	}85	resp.header.add_custom('ETag', '"3b1f"') or {}86	return resp87}8889// Starts the probe server on the first free port, so parallel test runs do not90// fight over a fixed one.91fn start_probe_server() !(int, &http.Server) {92	for port in [19301, 19302, 19303, 19304, 19305] {93		listener := net.listen_tcp(.ip, '127.0.0.1:${port}') or { continue }94		mut server := &http.Server{95			listener:             listener96			handler:              EtagHandler{}97			show_startup_message: false98		}99		spawn server.listen_and_serve()100		server.wait_till_running()!101		return port, server102	}103	return error('no free port for the probe server')104}105106// The S3 backend needs custom request headers, the response status, and the107// ETag that conditional writes are keyed on. TLS itself is exercised against a108// live bucket, not here.109fn test_http_client_sends_headers_and_reads_status_and_etag() {110	port, mut server := start_probe_server()!111	defer {112		server.stop()113	}114	url := 'http://127.0.0.1:${port}/probe'115	mut hdr := http.new_header()116	hdr.add_custom('If-None-Match', '*')!117118	resp := http.fetch(119		url:    url120		method: .get121		header: hdr122	)!123	assert resp.status_code == 412124	assert resp.header.get_custom('ETag', http.HeaderQueryConfig{}) or { '' } == '"3b1f"'125126	plain := http.fetch(url: url, method: .get)!127	assert plain.status_code == 200128	assert plain.body == 'payload'129130	// Retries: the S3 backend retries network errors and 5xx. A dead port must131	// surface as an error rather than a hang.132	if _ := http.fetch(133		url:          'http://127.0.0.1:19399/gone'134		method:       .get135		read_timeout: 2 * time.second136	)137	{138		assert false, 'a request to a closed port should fail'139	}140}