"""Unit tests for crawler_core.qcwy.sign — Job51Sign. All tests are pure function assertions: no HTTP, no network, no mocks. """ import sys import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) import re import pytest from crawler_core.qcwy.sign import Job51Sign, SIGN_KEY class TestJob51SignInit: def test_default_sign_key(self): signer = Job51Sign() assert signer.sign_key == SIGN_KEY assert len(SIGN_KEY) == 64 # 64-char hex key def test_custom_sign_key(self): custom_key = "a" * 64 signer = Job51Sign(sign_key=custom_key) assert signer.sign_key == custom_key class TestJob51SignBuildSignPath: def setup_method(self): self.signer = Job51Sign() def test_returns_tuple_of_two_strings(self): result = self.signer.build_sign_path("open/test") assert isinstance(result, tuple) assert len(result) == 2 assert all(isinstance(s, str) for s in result) def test_get_path_format(self): path, sign = self.signer.build_sign_path("open/test", "GET") assert path.startswith("/open/test?api_key=51job×tamp="), \ f"Path format wrong: {path}" def test_sign_hex_length(self): _, sign = self.signer.build_sign_path("open/test") assert len(sign) == 64, f"Sign should be 64-char hex, got {len(sign)}: {sign}" def test_sign_hex_format(self): _, sign = self.signer.build_sign_path("open/test") assert re.match(r'^[0-9a-f]{64}$', sign), f"Sign not hex: {sign}" def test_get_vs_post_different_sign(self): _, get_sign = self.signer.build_sign_path("open/test", "GET") _, post_sign = self.signer.build_sign_path("open/test", "POST", body={"k": "v"}) assert get_sign != post_sign, "GET and POST should produce different signatures" def test_get_with_params_includes_params_in_path(self): path, _ = self.signer.build_sign_path("open/test", "GET", params={"city": "shanghai"}) assert "city" in path and "shanghai" in path, \ f"Params should appear in path: {path}" def test_sign_key_in_path(self): path, _ = self.signer.build_sign_path("open/jobs") assert "api_key=51job" in path, f"api_key=51job missing from path: {path}" class TestJob51SignGenerateUuid: def test_generate_uuid_is_string(self): uuid = Job51Sign.generate_uuid() assert isinstance(uuid, str) def test_generate_uuid_length(self): uuid = Job51Sign.generate_uuid() # 13-char ms timestamp + 10-char random int = 23 chars assert len(uuid) == 23, f"Expected 23 chars, got {len(uuid)}: {uuid}" def test_generate_uuid_numeric(self): uuid = Job51Sign.generate_uuid() assert uuid.isdigit(), f"UUID should be all digits: {uuid}"