"""gpt-image-2 尺寸与参数约束的测试。 每个用例对应官方文档里的一条约束。这些约束若搞错,表现是用户提交后拿到一个失败 任务(服务商 400),而本地测试全绿——所以值得逐条钉住。 """ from __future__ import annotations import pytest from app.providers.openai.capabilities import ( parse_size, validate_output_format, validate_quality, validate_size, ) class TestStandardSizesAndAutoAlwaysPass: """文档明示支持的取值不做算术校验。""" def test_standard_sizes(self): for size in ("1024x1024", "1536x1024", "1024x1536"): validate_size(size) def test_auto(self): validate_size("auto") def test_none(self): validate_size(None) class TestEdgeMultipleOf16: """官方:宽高都必须能被 16 整除。""" def test_both_divisible_passes(self): validate_size("1536x864") # 文档给的例子 def test_width_not_divisible_is_rejected(self): with pytest.raises(ValueError, match="16 整除"): validate_size("1000x864") def test_height_not_divisible_is_rejected(self): with pytest.raises(ValueError, match="16 整除"): validate_size("1536x865") class TestAspectRatioBounds: """官方:宽高比必须在 1:3 ~ 3:1 之间。""" def test_three_to_one_is_allowed(self): # 2880x960 = 3:1,且都能被 16 整除。 validate_size("2880x960") def test_one_to_three_is_allowed(self): validate_size("960x2880") def test_portrait_at_the_pixel_limit_is_allowed(self): """竖版 4k(2160x3840)与横版同像素量,必须放行。 按「逐边」理解上限会禁掉所有高于 2160 的竖图,而 1024x1536 是文档明示的 标准竖图尺寸——故逐边理解必然过严。见 capabilities 里的说明。 """ validate_size("2160x3840") def test_wider_than_three_to_one_is_rejected(self): # 3840x960 = 4:1。 with pytest.raises(ValueError, match="宽高比"): validate_size("3840x960") def test_taller_than_one_to_three_is_rejected(self): # 640x2880 = 1:4.5,且在像素上限之内,故触发的必须是宽高比那条。 with pytest.raises(ValueError, match="宽高比"): validate_size("640x2880") class TestMaximumResolution: def test_the_maximum_itself_is_allowed(self): validate_size("3840x2160") def test_longest_edge_beyond_the_cap_is_rejected(self): with pytest.raises(ValueError, match="最长边"): validate_size("4096x2048") def test_within_edge_cap_but_beyond_the_pixel_cap_is_rejected(self): # 3840x2688:最长边合规、宽高比合规,但总像素超了。 with pytest.raises(ValueError, match="总像素"): validate_size("3840x2688") def test_a_tall_portrait_within_both_caps_passes(self): validate_size("1024x2176") class TestMalformedSize: def test_not_a_size_string(self): with pytest.raises(ValueError, match="形态非法"): validate_size("large") def test_missing_separator(self): with pytest.raises(ValueError, match="形态非法"): validate_size("1024") def test_parse_returns_none_for_garbage(self): assert parse_size("nonsense") is None def test_parse_reads_a_valid_pair(self): size = parse_size("1536x864") assert (size.width, size.height) == (1536, 864) assert str(size) == "1536x864" class TestQuality: def test_supported_values_pass(self): for quality in ("low", "medium", "high", "auto"): validate_quality(quality) def test_xhigh_is_rejected_because_it_is_2_5_only(self): # 官方:xhigh 与 max 仅 gpt-image-2.5 系列支持。 with pytest.raises(ValueError, match="xhigh"): validate_quality("xhigh") def test_max_is_rejected_too(self): with pytest.raises(ValueError): validate_quality("max") def test_none_passes(self): validate_quality(None) class TestOutputFormat: def test_supported_formats(self): for fmt in ("png", "jpeg", "webp"): validate_output_format(fmt) def test_unknown_format_is_rejected(self): with pytest.raises(ValueError, match="非法"): validate_output_format("gif") def test_jpeg_with_transparent_background_is_rejected(self): """jpeg 没有 alpha 通道。 服务商不会因此报错——只会给一张背景被填成不透明的图,而那是**静默的** 不符合预期,比报错难查得多。 """ with pytest.raises(ValueError, match="alpha"): validate_output_format("jpeg", transparent=True) def test_png_with_transparent_background_is_fine(self): validate_output_format("png", transparent=True) validate_output_format("webp", transparent=True)