OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer / integration / layer1_smoke.gd

154 lines · gd ref:
extends SceneTree
## Layer 1: GDExtension Load + P2P API Smoke
##
## Tests that libhyperswarm_peer.so loads in Godot 4.6 headless,
## all P2P-relevant methods and signals exist, and keypair generation works.
## Single process, no network.

var _pass := 0
var _fail := 0
var _total := 0


func _init() -> void:
	_run_tests()
	_report()
	quit()


func _run_tests() -> void:
	# Test 1: ClassDB can instantiate
	_test("ClassDB can instantiate HyperswarmMultiplayerPeer",
		ClassDB.can_instantiate(&"HyperswarmMultiplayerPeer"))

	# Test 2: Instantiate and verify P2P methods exist
	var peer := ClassDB.instantiate(&"HyperswarmMultiplayerPeer") as MultiplayerPeerExtension
	_test("Instantiation returns non-null", peer != null)
	if peer == null:
		_skip_rest("Cannot instantiate — aborting remaining tests")
		return

	# DHT methods
	_test("has generate_dht_keypair()", peer.has_method("generate_dht_keypair"))
	_test("has join_dht()", peer.has_method("join_dht"))
	_test("has listen_on_dht()", peer.has_method("listen_on_dht"))
	_test("has connect_to_peer()", peer.has_method("connect_to_peer"))
	_test("has announce_topic()", peer.has_method("announce_topic"))
	_test("has lookup_topic()", peer.has_method("lookup_topic"))
	_test("has join_topic()", peer.has_method("join_topic"))
	_test("has leave_topic()", peer.has_method("leave_topic"))
	_test("has get_dht_status()", peer.has_method("get_dht_status"))
	_test("has get_peer_public_key()", peer.has_method("get_peer_public_key"))

	# DHT storage methods
	_test("has dht_put()", peer.has_method("dht_put"))
	_test("has dht_get()", peer.has_method("dht_get"))
	_test("has dht_mutable_put()", peer.has_method("dht_mutable_put"))
	_test("has dht_mutable_get()", peer.has_method("dht_mutable_get"))

	# Encryption methods
	_test("has generate_keypair()", peer.has_method("generate_keypair"))
	_test("has create_server_with_keypair()", peer.has_method("create_server_with_keypair"))
	_test("has create_client_with_key()", peer.has_method("create_client_with_key"))

	# Basic networking
	_test("has create_server()", peer.has_method("create_server"))
	_test("has create_client()", peer.has_method("create_client"))
	_test("has get_stats()", peer.has_method("get_stats"))

	# Test 3: generate_dht_keypair() returns correct format
	var dht_kp: Dictionary = peer.generate_dht_keypair()
	_test("generate_dht_keypair() returns Dictionary", dht_kp is Dictionary)
	_test("dht_kp has public_key", dht_kp.has("public_key"))
	_test("dht_kp has secret_key", dht_kp.has("secret_key"))
	if dht_kp.has("public_key") and dht_kp.has("secret_key"):
		var dht_pk: PackedByteArray = dht_kp["public_key"]
		var dht_sk: PackedByteArray = dht_kp["secret_key"]
		_test("dht public_key is 32 bytes", dht_pk.size() == 32)
		_test("dht secret_key is 64 bytes", dht_sk.size() == 64)
		_test("dht public_key is non-zero", not _is_zero(dht_pk))

	# Test 4: generate_keypair() (Curve25519) returns correct format
	var kp: Dictionary = peer.generate_keypair()
	_test("generate_keypair() returns Dictionary", kp is Dictionary)
	_test("kp has public_key", kp.has("public_key"))
	_test("kp has secret_key", kp.has("secret_key"))
	if kp.has("public_key") and kp.has("secret_key"):
		var pk: PackedByteArray = kp["public_key"]
		var sk: PackedByteArray = kp["secret_key"]
		_test("curve25519 public_key is 32 bytes", pk.size() == 32)
		_test("curve25519 secret_key is 32 bytes", sk.size() == 32)
		_test("curve25519 public_key is non-zero", not _is_zero(pk))

	# Two keypairs should be different
	var kp2: Dictionary = peer.generate_keypair()
	if kp.has("public_key") and kp2.has("public_key"):
		_test("two keypairs differ", kp["public_key"] != kp2["public_key"])

	# Test 5: Verify signals registered
	_test("signal dht_peer_found", peer.has_signal("dht_peer_found"))
	_test("signal dht_put_complete", peer.has_signal("dht_put_complete"))
	_test("signal dht_get_complete", peer.has_signal("dht_get_complete"))
	_test("signal dht_mutable_put_complete", peer.has_signal("dht_mutable_put_complete"))
	_test("signal dht_mutable_get_complete", peer.has_signal("dht_mutable_get_complete"))
	# Standard MultiplayerPeer signals
	_test("signal peer_connected", peer.has_signal("peer_connected"))
	_test("signal peer_disconnected", peer.has_signal("peer_disconnected"))

	# Test 6: Swarm Manager API methods exist
	_test("has swarm_join()", peer.has_method("swarm_join"))
	_test("has swarm_leave()", peer.has_method("swarm_leave"))
	_test("has swarm_join_peer()", peer.has_method("swarm_join_peer"))
	_test("has swarm_leave_peer()", peer.has_method("swarm_leave_peer"))
	_test("has get_swarm_status()", peer.has_method("get_swarm_status"))
	_test("swarm_connection signal exists", peer.has_signal("swarm_connection"))

	# Test 7: get_swarm_status() returns valid dict before DHT active
	var ss: Dictionary = peer.get_swarm_status()
	_test("swarm status has peers_known", ss.has("peers_known"))
	_test("swarm status peers_known is 0 initially", ss.get("peers_known", -1) == 0)
	_test("swarm status has max_peers", ss.get("max_peers", 0) == 64)
	_test("swarm status has max_parallel", ss.get("max_parallel", 0) == 3)
	_test("swarm status has explicit_count", ss.has("explicit_count"))

	# Test 8: get_stats() works before any connection
	var stats: Dictionary = peer.get_stats()
	_test("get_stats() returns Dictionary", stats is Dictionary)
	_test("stats has connection_status", stats.has("connection_status"))

	# Test 7: get_dht_status() works before joining
	var dht_status: Dictionary = peer.get_dht_status()
	_test("get_dht_status() returns Dictionary", dht_status is Dictionary)

	# Test 8: Close with no crash
	peer.close()
	_test("close() without crash", true)


func _test(name: String, condition: bool) -> void:
	_total += 1
	if condition:
		_pass += 1
		print("  PASS  %s" % name)
	else:
		_fail += 1
		print("  FAIL  %s" % name)


func _skip_rest(reason: String) -> void:
	print("  SKIP  %s" % reason)


func _is_zero(arr: PackedByteArray) -> bool:
	for b: int in arr:
		if b != 0:
			return false
	return true


func _report() -> void:
	print("")
	print("=== Layer 1: %d/%d passed ===" % [_pass, _total])
	if _fail > 0:
		print("FAILED: %d tests" % _fail)