library_check_test.v

4.3 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// Vectors from the official blake3 bindings, cross-checked against the20// naming of real Koh 2 objects: `.koh/objects/<hash[0..2]>/<hash[2..]>` is21// blake3(uncompressed bytes) of a zlib stream.22fn test_blake3_vectors() {23	assert blake3.sum256(''.bytes()).hex() == 'af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262'24	assert blake3.sum256('abc'.bytes()).hex() == '6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85'25	assert blake3.sum256('The quick brown fox jumps over the lazy dog'.bytes()).hex() == '2f1514181aadccd913abd94cfa592701a5686ab23f8df1dff1b74710febc6d4a'26}2728fn test_blake3_streaming_matches_one_shot() {29	mut d := blake3.Digest.new_hash()!30	d.write('The quick brown '.bytes())!31	d.write('fox jumps over the lazy dog'.bytes())!32	assert d.checksum(32) == blake3.sum256('The quick brown fox jumps over the lazy dog'.bytes())33}3435fn test_zlib_round_trip() {36	// Object bytes must survive a trip through the store byte for byte,37	// including an empty payload and one with every byte value.38	mut all_bytes := []u8{len: 256}39	for i in 0 .. 256 {40		all_bytes[i] = u8(i)41	}42	mut payloads := [[]u8{}, 'hello koh'.bytes()]43	payloads << all_bytes44	for payload in payloads {45		packed := zlib.compress(payload)!46		assert zlib.decompress(packed)! == payload47	}48}4950fn test_sha256_and_hmac_vectors() {51	assert sha256.sum256('abc'.bytes()).hex() == 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'52	assert sha256.hexhash('abc') == 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'53	// HMAC-SHA256, the primitive behind SigV4 signing.54	mac := hmac.new('key'.bytes(), 'The quick brown fox jumps over the lazy dog'.bytes(),55		sha256.sum256, 64)56	assert mac.hex() == 'f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8'57}5859struct EtagHandler {60mut:61	seen_if_none_match string62}6364fn (mut h EtagHandler) handle(req http.Request) http.Response {65	h.seen_if_none_match = req.header.get_custom('If-None-Match', http.HeaderQueryConfig{}) or {66		''67	}68	mut resp := http.Response{69		body: 'payload'70	}71	if h.seen_if_none_match == '*' {72		resp.status_code = 41273		resp.status_msg = 'Precondition Failed'74	} else {75		resp.status_code = 20076		resp.status_msg = 'OK'77	}78	resp.header.add_custom('ETag', '"3b1f"') or {}79	return resp80}8182// Starts the probe server on the first free port, so parallel test runs do not83// fight over a fixed one.84fn start_probe_server() !(int, &http.Server) {85	for port in [19301, 19302, 19303, 19304, 19305] {86		listener := net.listen_tcp(.ip, '127.0.0.1:${port}') or { continue }87		mut server := &http.Server{88			listener:             listener89			handler:              EtagHandler{}90			show_startup_message: false91		}92		spawn server.listen_and_serve()93		server.wait_till_running()!94		return port, server95	}96	return error('no free port for the probe server')97}9899// The S3 backend needs custom request headers, the response status, and the100// ETag that conditional writes are keyed on. TLS itself is exercised against a101// live bucket, not here.102fn test_http_client_sends_headers_and_reads_status_and_etag() {103	port, mut server := start_probe_server()!104	defer {105		server.stop()106	}107	url := 'http://127.0.0.1:${port}/probe'108	mut hdr := http.new_header()109	hdr.add_custom('If-None-Match', '*')!110111	resp := http.fetch(112		url:    url113		method: .get114		header: hdr115	)!116	assert resp.status_code == 412117	assert resp.header.get_custom('ETag', http.HeaderQueryConfig{}) or { '' } == '"3b1f"'118119	plain := http.fetch(url: url, method: .get)!120	assert plain.status_code == 200121	assert plain.body == 'payload'122123	// Retries: the S3 backend retries network errors and 5xx. A dead port must124	// surface as an error rather than a hang.125	if _ := http.fetch(126		url:          'http://127.0.0.1:19399/gone'127		method:       .get128		read_timeout: 2 * time.second129	)130	{131		assert false, 'a request to a closed port should fail'132	}133}