Files
modelscope--funasr/tests/test_collect_growth_metrics.py
2026-07-13 13:25:10 +08:00

1552 lines
62 KiB
Python

import importlib.util
import io
import json
import subprocess
import sys
from datetime import date, datetime, timezone
from contextlib import redirect_stdout
from pathlib import Path
def load_growth_metrics_module():
script_path = Path(__file__).resolve().parents[1] / "scripts" / "collect_growth_metrics.py"
spec = importlib.util.spec_from_file_location("collect_growth_metrics", script_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_default_integration_prs_include_sglang_omni_fun_asr():
module = load_growth_metrics_module()
assert "sgl-project/sglang-omni#898" in module.DEFAULT_INTEGRATION_PRS
def test_github_headers_falls_back_to_gh_auth_token(monkeypatch):
module = load_growth_metrics_module()
monkeypatch.delenv("GITHUB_TOKEN", raising=False)
monkeypatch.setenv("HOME", "/tmp/funasr-growth-metrics-no-token-home")
def fake_run(args, **kwargs):
assert args == ["gh", "auth", "token"]
return subprocess.CompletedProcess(args=args, returncode=0, stdout="cli-token\n")
monkeypatch.setattr(module.subprocess, "run", fake_run)
headers = module.github_headers()
assert headers["Authorization"] == "Bearer cli-token"
def test_github_headers_reads_funasr_ops_token_file(monkeypatch, tmp_path):
module = load_growth_metrics_module()
monkeypatch.delenv("GITHUB_TOKEN", raising=False)
monkeypatch.setenv("HOME", str(tmp_path))
token_path = tmp_path / ".config" / "funasr-ops" / "github_token"
token_path.parent.mkdir(parents=True)
token_path.write_text("file-token\n")
def fake_run(args, **kwargs):
raise FileNotFoundError("gh")
monkeypatch.setattr(module.subprocess, "run", fake_run)
headers = module.github_headers()
assert headers["Authorization"] == "Bearer file-token"
def test_default_integration_prs_include_high_visibility_external_queue():
module = load_growth_metrics_module()
expected_prs = {
"infiniflow/ragflow#16473",
"pipecat-ai/pipecat#4844",
"TEN-framework/ten-framework#2191",
"activepieces/activepieces#13985",
"Uberi/speech_recognition#903",
}
assert expected_prs.issubset(set(module.DEFAULT_INTEGRATION_PRS))
def test_default_integration_prs_include_new_growth_lanes():
module = load_growth_metrics_module()
expected_prs = {
"Significant-Gravitas/AutoGPT#13500",
"huggingface/speech-to-speech#319",
"run-llama/llama_index#21958",
"run-llama/llama_index#21996",
"mem0ai/mem0#5571",
"mudler/LocalAI#10090",
"agno-agi/agno#8501",
"GetStream/Vision-Agents#606",
"ai4s-research/awesome-ai-for-science#69",
}
assert expected_prs.issubset(set(module.DEFAULT_INTEGRATION_PRS))
def test_default_integration_prs_include_video_discovery_lanes():
module = load_growth_metrics_module()
expected_prs = {
"tmoroney/auto-subs#629",
"mahseema/awesome-ai-tools#1689",
}
assert expected_prs.issubset(set(module.DEFAULT_INTEGRATION_PRS))
def test_default_integration_prs_include_voice_agent_and_ml_discovery_lanes():
module = load_growth_metrics_module()
expected_prs = {
"livekit/agents#6176",
"lukasmasuch/best-of-ml-python#455",
}
assert expected_prs.issubset(set(module.DEFAULT_INTEGRATION_PRS))
def test_default_integration_prs_include_high_star_awesome_discovery_lanes():
module = load_growth_metrics_module()
expected_prs = {
"vinta/awesome-python#3246",
"fighting41love/funNLP#478",
"josephmisiti/awesome-machine-learning#1339",
"RVC-Boss/GPT-SoVITS#2801",
"jobbole/awesome-python-cn#141",
"ChristosChristofidis/awesome-deep-learning#317",
"Hannibal046/Awesome-LLM#623",
"AiHubCN/Awesome-Chinese-LLM#103",
"pluja/awesome-privacy#836",
"BradyFU/Awesome-Multimodal-Large-Language-Models#280",
"mahmoud/awesome-python-applications#227",
"bharathgs/Awesome-pytorch-list#164",
"owainlewis/awesome-artificial-intelligence#243",
"steven2358/awesome-generative-ai#821",
"WangRongsheng/awesome-LLM-resources#162",
"crownpku/Awesome-Chinese-NLP#32",
}
assert expected_prs.issubset(set(module.DEFAULT_INTEGRATION_PRS))
def test_high_star_awesome_discovery_lanes_wait_for_maintainer_review():
module = load_growth_metrics_module()
expected_prs = {
"vinta/awesome-python#3246",
"fighting41love/funNLP#478",
"josephmisiti/awesome-machine-learning#1339",
"RVC-Boss/GPT-SoVITS#2801",
"jobbole/awesome-python-cn#141",
"ChristosChristofidis/awesome-deep-learning#317",
"Hannibal046/Awesome-LLM#623",
"AiHubCN/Awesome-Chinese-LLM#103",
"pluja/awesome-privacy#836",
"BradyFU/Awesome-Multimodal-Large-Language-Models#280",
"mahmoud/awesome-python-applications#227",
"bharathgs/Awesome-pytorch-list#164",
"owainlewis/awesome-artificial-intelligence#243",
"steven2358/awesome-generative-ai#821",
"WangRongsheng/awesome-LLM-resources#162",
"crownpku/Awesome-Chinese-NLP#32",
}
assert expected_prs.issubset(set(module.KNOWN_ASSISTED_REVIEW_REQUESTS))
def test_default_integration_prs_include_missing_validated_discovery_lanes():
module = load_growth_metrics_module()
expected_prs = {
"mahseema/awesome-ai-tools#1403",
"INTERMT/Awesome-PyTorch-Chinese#5",
"krzjoa/awesome-python-data-science#99",
"zzw922cn/awesome-speech-recognition-speech-synthesis-papers#27",
"Osmantic/ODS#1639",
"faroit/awesome-python-scientific-audio#85",
"joewongjc/type4me#207",
"ga642381/speech-trident#31",
}
assert expected_prs.issubset(set(module.DEFAULT_INTEGRATION_PRS))
def test_missing_validated_discovery_lanes_wait_for_maintainer_review():
module = load_growth_metrics_module()
expected_prs = {
"mahseema/awesome-ai-tools#1403",
"INTERMT/Awesome-PyTorch-Chinese#5",
"krzjoa/awesome-python-data-science#99",
"zzw922cn/awesome-speech-recognition-speech-synthesis-papers#27",
"Osmantic/ODS#1639",
"faroit/awesome-python-scientific-audio#85",
"joewongjc/type4me#207",
"ga642381/speech-trident#31",
}
assert expected_prs.issubset(set(module.KNOWN_ASSISTED_REVIEW_REQUESTS))
def test_default_integration_prs_include_mcp_discovery_lanes():
module = load_growth_metrics_module()
assert "punkpeye/awesome-mcp-servers#7153" in module.DEFAULT_INTEGRATION_PRS
def test_default_integration_prs_include_speech_server_lanes():
module = load_growth_metrics_module()
expected_prs = {
"speaches-ai/speaches#658",
"getpaseo/paseo#1634",
}
assert expected_prs.issubset(set(module.DEFAULT_INTEGRATION_PRS))
def test_default_integration_prs_exclude_archived_integration_prs():
module = load_growth_metrics_module()
assert "xinnan-tech/xiaozhi-esp32-server#3255" not in module.DEFAULT_INTEGRATION_PRS
def test_collect_github_repo_metrics_splits_open_issues_and_pull_requests(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/modelscope/FunASR":
return {
"stargazers_count": 18716,
"forks_count": 3100,
"subscribers_count": 210,
"open_issues_count": 5,
"default_branch": "main",
"pushed_at": "2026-06-30T02:26:33Z",
"html_url": "https://github.com/modelscope/FunASR",
}
if url == "https://api.github.com/repos/modelscope/FunASR/pulls?state=open&per_page=100":
return [{"number": 3056}, {"number": 3057}]
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_github_repo_metrics("modelscope/FunASR")
assert metrics["open_items"] == 5
assert metrics["open_pull_requests"] == 2
assert metrics["open_issues"] == 3
def test_collect_ecosystem_metrics_sums_repositories_and_target_gap(monkeypatch):
module = load_growth_metrics_module()
github_payloads = {
"modelscope/FunASR": {
"stargazers_count": 18714,
"forks_count": 3100,
"subscribers_count": 210,
"open_issues_count": 3,
"default_branch": "main",
"pushed_at": "2026-06-30T01:48:35Z",
"html_url": "https://github.com/modelscope/FunASR",
},
"FunAudioLLM/Fun-ASR": {
"stargazers_count": 1318,
"forks_count": 120,
"subscribers_count": 20,
"open_issues_count": 0,
"default_branch": "main",
"pushed_at": "2026-06-29T13:24:05Z",
"html_url": "https://github.com/FunAudioLLM/Fun-ASR",
},
"FunAudioLLM/SenseVoice": {
"stargazers_count": 8718,
"forks_count": 800,
"subscribers_count": 90,
"open_issues_count": 0,
"default_branch": "main",
"pushed_at": "2026-06-29T13:24:06Z",
"html_url": "https://github.com/FunAudioLLM/SenseVoice",
},
"modelscope/FunClip": {
"stargazers_count": 5872,
"forks_count": 500,
"subscribers_count": 70,
"open_issues_count": 0,
"default_branch": "main",
"pushed_at": "2026-06-29T20:47:23Z",
"html_url": "https://github.com/modelscope/FunClip",
},
}
def fake_fetch_json(url, headers=None):
if url.endswith("/pulls?state=open&per_page=100"):
return []
if url.startswith("https://api.github.com/repos/"):
repo = url.removeprefix("https://api.github.com/repos/")
return github_payloads[repo]
if url == "https://pypi.org/pypi/funasr/json":
return {"info": {"version": "1.3.14", "summary": "FunASR", "project_url": "https://pypi.org/project/funasr/"}}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_ecosystem_metrics(
["modelscope/FunASR", "FunAudioLLM/Fun-ASR", "FunAudioLLM/SenseVoice", "modelscope/FunClip"],
package="funasr",
baseline_stars=31224,
target_additional_stars=20000,
)
assert metrics["ecosystem"]["total_stars"] == 34622
assert metrics["ecosystem"]["baseline_stars"] == 31224
assert metrics["ecosystem"]["added_stars"] == 3398
assert metrics["ecosystem"]["remaining_to_target"] == 16602
assert [repo["repo"] for repo in metrics["ecosystem"]["repositories"]] == [
"modelscope/FunASR",
"FunAudioLLM/Fun-ASR",
"FunAudioLLM/SenseVoice",
"modelscope/FunClip",
]
def test_collect_ecosystem_metrics_calculates_daily_target(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url.endswith("/pulls?state=open&per_page=100"):
return []
if url.startswith("https://api.github.com/repos/"):
repo = url.removeprefix("https://api.github.com/repos/")
stars = {
"modelscope/FunASR": 18715,
"FunAudioLLM/Fun-ASR": 1318,
"FunAudioLLM/SenseVoice": 8718,
"modelscope/FunClip": 5872,
}[repo]
return {
"stargazers_count": stars,
"forks_count": 0,
"subscribers_count": 0,
"open_issues_count": 0,
"default_branch": "main",
"pushed_at": "2026-06-30T00:00:00Z",
"html_url": f"https://github.com/{repo}",
}
if url == "https://pypi.org/pypi/funasr/json":
return {"info": {"version": "1.3.14", "summary": "FunASR", "project_url": "https://pypi.org/project/funasr/"}}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_ecosystem_metrics(
["modelscope/FunASR", "FunAudioLLM/Fun-ASR", "FunAudioLLM/SenseVoice", "modelscope/FunClip"],
package="funasr",
baseline_stars=31224,
target_additional_stars=20000,
target_date="2026-09-30",
today=date(2026, 6, 30),
)
assert metrics["ecosystem"]["target_date"] == "2026-09-30"
assert metrics["ecosystem"]["days_remaining"] == 92
assert metrics["ecosystem"]["required_daily_average"] == 181
def test_collect_integration_metrics_summarizes_pull_request_checks(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/huggingface/transformers/pulls/46180":
return {
"number": 46180,
"title": "Add Fun-ASR-Nano model",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "clean",
"html_url": "https://github.com/huggingface/transformers/pull/46180",
"updated_at": "2026-06-29T23:45:57Z",
"head": {"sha": "8a744ec", "ref": "add-fun-asr-nano"},
"base": {"ref": "main"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/huggingface/transformers/commits/8a744ec/status":
return {"state": "success", "statuses": []}
if url == "https://api.github.com/repos/huggingface/transformers/commits/8a744ec/check-runs?per_page=100":
return {
"total_count": 3,
"check_runs": [
{"name": "pr-ci / Check code quality", "status": "completed", "conclusion": "success"},
{
"name": "pr-ci / tests_processors / tests_processors [shard 3/8]",
"status": "completed",
"conclusion": "failure",
"html_url": "https://github.com/huggingface/transformers/actions/runs/1/job/2",
},
{"name": "pr-ci / slow tests", "status": "queued", "conclusion": None},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(
["huggingface/transformers#46180"],
now=datetime(2026, 7, 2, 0, 0, 0, tzinfo=timezone.utc),
)
integration = metrics["integrations"][0]
assert integration["pr"] == "huggingface/transformers#46180"
assert integration["title"] == "Add Fun-ASR-Nano model"
assert integration["next_action"] == "fix checks"
assert integration["updated_age_days"] == 2
assert integration["mergeable"] is True
assert integration["checks"]["state"] == "failure"
assert integration["checks"]["total_check_runs"] == 3
assert integration["checks"]["failed_check_runs"] == [
{
"name": "pr-ci / tests_processors / tests_processors [shard 3/8]",
"conclusion": "failure",
"url": "https://github.com/huggingface/transformers/actions/runs/1/job/2",
}
]
assert integration["checks"]["pending_check_runs"] == [
{"name": "pr-ci / slow tests", "status": "queued", "url": None}
]
def test_collect_integration_metrics_classifies_known_external_ci_failure(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/huggingface/transformers/pulls/46180":
return {
"number": 46180,
"title": "Add Fun-ASR-Nano model",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "blocked",
"html_url": "https://github.com/huggingface/transformers/pull/46180",
"updated_at": "2026-06-30T04:24:15Z",
"head": {"sha": "8ed336b", "ref": "add-fun-asr-nano"},
"base": {"ref": "main"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/huggingface/transformers/commits/8ed336b/status":
return {"state": "success", "statuses": []}
if url == "https://api.github.com/repos/huggingface/transformers/commits/8ed336b/check-runs?per_page=100":
return {
"total_count": 99,
"check_runs": [
{
"name": "pr-ci / tests_processors / tests_processors [shard 3/8]",
"status": "completed",
"conclusion": "failure",
"html_url": "https://github.com/huggingface/transformers/actions/runs/28419525291/job/84209871338",
},
{
"name": "pr-ci / PR CI status",
"status": "completed",
"conclusion": "failure",
"html_url": "https://github.com/huggingface/transformers/actions/runs/28419525291/job/84211351756",
},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["huggingface/transformers#46180"])
integration = metrics["integrations"][0]
assert integration["checks"]["state"] == "failure"
assert integration["known_external_failure_reason"] == (
"LightOnOCR shared hub-cache read-only failure; PR CI status is the aggregate failure"
)
assert integration["known_external_failure_action"] == "wait for maintainer rerun"
assert integration["next_action"] == "wait for maintainer rerun"
def test_collect_integration_metrics_handles_known_external_ci_aggregate_race(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/huggingface/transformers/pulls/46180":
return {
"number": 46180,
"title": "Add Fun-ASR-Nano model",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "blocked",
"html_url": "https://github.com/huggingface/transformers/pull/46180",
"updated_at": "2026-06-30T04:15:07Z",
"head": {"sha": "8ed336b", "ref": "add-fun-asr-nano"},
"base": {"ref": "main"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/huggingface/transformers/commits/8ed336b/status":
return {"state": "success", "statuses": []}
if url == "https://api.github.com/repos/huggingface/transformers/commits/8ed336b/check-runs?per_page=100":
return {
"total_count": 98,
"check_runs": [
{
"name": "pr-ci / tests_processors / tests_processors [shard 3/8]",
"status": "completed",
"conclusion": "failure",
"html_url": "https://github.com/huggingface/transformers/actions/runs/28419525291/job/84209871338",
},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["huggingface/transformers#46180"])
integration = metrics["integrations"][0]
assert integration["known_external_failure_reason"] == (
"LightOnOCR shared hub-cache read-only failure; PR CI status is the aggregate failure"
)
assert integration["known_external_failure_action"] == "wait for maintainer rerun"
assert integration["next_action"] == "wait for maintainer rerun"
def test_format_ecosystem_markdown_includes_open_pull_requests():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-06-30T02:30:00+00:00",
"ecosystem": {
"repositories": [
{
"repo": "modelscope/FunASR",
"stars": 18716,
"forks": 3100,
"open_issues": 3,
"open_pull_requests": 2,
"pushed_at": "2026-06-30T02:26:33Z",
"html_url": "https://github.com/modelscope/FunASR",
}
],
"total_stars": 18716,
"baseline_stars": 31224,
"added_stars": -12508,
"target_additional_stars": 20000,
"remaining_to_target": 32508,
"target_date": "2026-09-30",
"days_remaining": 92,
"required_daily_average": 354,
},
"pypi": {"package": "funasr", "version": "1.3.14", "project_url": "https://pypi.org/project/funasr/"},
}
output = module.format_ecosystem_markdown(metrics)
assert "| Repository | Stars | Forks | Open issues | Open PRs | Last push |" in output
assert "| [modelscope/FunASR](https://github.com/modelscope/FunASR) | 18,716 | 3,100 | 3 | 2 |" in output
def test_collect_integration_metrics_recommends_review_for_clean_success_pr(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/example/clean-pr/pulls/42":
return {
"number": 42,
"title": "Add FunASR",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "clean",
"html_url": "https://github.com/example/clean-pr/pull/42",
"updated_at": "2026-06-30T06:44:34Z",
"head": {"sha": "9a19b5e", "ref": "add-funasr"},
"base": {"ref": "main"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/example/clean-pr/commits/9a19b5e/status":
return {"state": "success", "statuses": []}
if url == "https://api.github.com/repos/example/clean-pr/commits/9a19b5e/check-runs?per_page=100":
return {
"total_count": 1,
"check_runs": [
{"name": "validate", "status": "completed", "conclusion": "success"},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["example/clean-pr#42"])
assert metrics["integrations"][0]["next_action"] == "request review"
def test_known_assisted_review_requests_include_validated_discovery_prs():
module = load_growth_metrics_module()
expected_prs = {
"mahseema/awesome-ai-tools#1689",
"ai4s-research/awesome-ai-for-science#69",
}
assert expected_prs.issubset(set(module.KNOWN_ASSISTED_REVIEW_REQUESTS))
def test_known_assisted_review_requests_include_validated_review_gate_prs():
module = load_growth_metrics_module()
expected_prs = {
"livekit/agents#6176",
"run-llama/llama_index#21958",
"run-llama/llama_index#21996",
"mudler/LocalAI#10090",
"ray-project/ray#64053",
"agno-agi/agno#8501",
"pipecat-ai/pipecat#4844",
"GetStream/Vision-Agents#606",
"Uberi/speech_recognition#903",
"huggingface/speech-to-speech#319",
"speaches-ai/speaches#658",
}
assert expected_prs.issubset(set(module.KNOWN_ASSISTED_REVIEW_REQUESTS))
def test_collect_integration_metrics_marks_known_assisted_review_request(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/lukasmasuch/best-of-ml-python/pulls/455":
return {
"number": 455,
"title": "Add FunASR",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "clean",
"html_url": "https://github.com/lukasmasuch/best-of-ml-python/pull/455",
"updated_at": "2026-06-30T09:16:59Z",
"head": {"sha": "48f3070", "ref": "add-funasr"},
"base": {"ref": "main"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/lukasmasuch/best-of-ml-python/commits/48f3070/status":
return {"state": "success", "statuses": []}
if url == "https://api.github.com/repos/lukasmasuch/best-of-ml-python/commits/48f3070/check-runs?per_page=100":
return {
"total_count": 1,
"check_runs": [
{"name": "tests", "status": "completed", "conclusion": "success"},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["lukasmasuch/best-of-ml-python#455"])
integration = metrics["integrations"][0]
assert integration["known_assisted_review_reason"] == "review evidence already posted; avoid duplicate pings"
assert integration["next_action"] == "wait for maintainer review"
def test_collect_integration_metrics_applies_known_review_gate(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/punkpeye/awesome-mcp-servers/pulls/7153":
return {
"number": 7153,
"title": "Add FunASR speech recognition MCP server",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "clean",
"html_url": "https://github.com/punkpeye/awesome-mcp-servers/pull/7153",
"updated_at": "2026-06-16T04:21:55Z",
"head": {"sha": "d28680e", "ref": "add-funasr-mcp-server"},
"base": {"ref": "main"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/punkpeye/awesome-mcp-servers/commits/d28680e/status":
return {"state": "success", "statuses": []}
if url == "https://api.github.com/repos/punkpeye/awesome-mcp-servers/commits/d28680e/check-runs?per_page=100":
return {
"total_count": 2,
"check_runs": [
{"name": "check-submission", "status": "completed", "conclusion": "success"},
{"name": "welcome", "status": "completed", "conclusion": "skipped"},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["punkpeye/awesome-mcp-servers#7153"])
integration = metrics["integrations"][0]
assert integration["checks"]["state"] == "success"
assert integration["known_review_gate_reason"] == "Glama listing and score badge required before review"
assert integration["next_action"] == "submit Glama"
def test_collect_integration_metrics_surfaces_pending_cla_status(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/activepieces/activepieces/pulls/13985":
return {
"number": 13985,
"title": "feat: add FunASR speech recognition piece",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "blocked",
"html_url": "https://github.com/activepieces/activepieces/pull/13985",
"updated_at": "2026-06-30T01:17:07Z",
"head": {"sha": "f9d22ee", "ref": "funasr/activepieces-13450-conflict-fix"},
"base": {"ref": "main"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/activepieces/activepieces/commits/f9d22ee/status":
return {
"state": "pending",
"statuses": [
{
"context": "license/cla",
"state": "pending",
"target_url": "https://cla-assistant.io/activepieces/activepieces?pullRequest=13985",
}
],
}
if url == "https://api.github.com/repos/activepieces/activepieces/commits/f9d22ee/check-runs?per_page=100":
return {
"total_count": 1,
"check_runs": [
{"name": "GitGuardian Security Checks", "status": "completed", "conclusion": "success"},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["activepieces/activepieces#13985"])
integration = metrics["integrations"][0]
assert integration["next_action"] == "wait for author CLA"
assert integration["checks"]["pending_check_runs"] == [
{
"name": "license/cla",
"status": "pending",
"url": "https://cla-assistant.io/activepieces/activepieces?pullRequest=13985",
}
]
def test_collect_integration_metrics_classifies_autogpt_process_gate(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/Significant-Gravitas/AutoGPT/pulls/13500":
return {
"number": 13500,
"title": "Support configurable transcription endpoints",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "blocked",
"html_url": "https://github.com/Significant-Gravitas/AutoGPT/pull/13500",
"updated_at": "2026-07-07T21:13:23Z",
"head": {"sha": "ed9f137", "ref": "codex/openai-compatible-stt"},
"base": {"ref": "dev"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/Significant-Gravitas/AutoGPT/commits/ed9f137/status":
return {
"state": "failure",
"statuses": [
{
"context": "Vercel",
"state": "failure",
"target_url": "https://vercel.com/git/authorize?team=Significant%20Gravitas",
},
{
"context": "license/cla",
"state": "pending",
"target_url": "https://cla-assistant.io/Significant-Gravitas/AutoGPT?pullRequest=13500",
},
],
}
if url == "https://api.github.com/repos/Significant-Gravitas/AutoGPT/commits/ed9f137/check-runs?per_page=100":
return {
"total_count": 4,
"check_runs": [
{"name": "check API types", "status": "completed", "conclusion": "success"},
{"name": "lint", "status": "completed", "conclusion": "success"},
{"name": "integration_test", "status": "completed", "conclusion": "success"},
{"name": "end-to-end tests", "status": "completed", "conclusion": "success"},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["Significant-Gravitas/AutoGPT#13500"])
integration = metrics["integrations"][0]
assert integration["known_review_gate_reason"] == (
"CLA must be completed by an authorized author; Vercel preview also requires AutoGPT team authorization"
)
assert integration["next_action"] == "wait for author CLA"
assert {check["name"] for check in integration["checks"]["failed_check_runs"]} == {"Vercel"}
assert {check["name"] for check in integration["checks"]["pending_check_runs"]} == {"license/cla"}
def test_collect_integration_metrics_classifies_review_bot_failure(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/TEN-framework/ten-framework/pulls/2191":
return {
"number": 2191,
"title": "feat: add funasr_asr_python local FunASR ASR extension",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "blocked",
"html_url": "https://github.com/TEN-framework/ten-framework/pull/2191",
"updated_at": "2026-06-29T23:02:41Z",
"head": {"sha": "43cbedc", "ref": "add-funasr-asr-extension"},
"base": {"ref": "main"},
"user": {"login": "LauraGPT"},
}
if url == "https://api.github.com/repos/TEN-framework/ten-framework/commits/43cbedc/status":
return {"state": "pending", "statuses": []}
if url == "https://api.github.com/repos/TEN-framework/ten-framework/commits/43cbedc/check-runs?per_page=100":
return {
"total_count": 1,
"check_runs": [
{
"name": "claude-review",
"status": "completed",
"conclusion": "failure",
"html_url": "https://github.com/TEN-framework/ten-framework/actions/runs/27985905295/job/82827178776",
},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["TEN-framework/ten-framework#2191"])
integration = metrics["integrations"][0]
assert integration["known_review_gate_reason"] == (
"Claude review action requires maintainer permissions for fork PRs"
)
assert integration["next_action"] == "wait for maintainer review"
assert integration["checks"]["failed_check_runs"][0]["name"] == "claude-review"
def test_collect_integration_metrics_classifies_vercel_authorization_gate(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/mem0ai/mem0/pulls/5571":
return {
"number": 5571,
"title": "feat: add optional FunASR audio transcription helper",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "blocked",
"html_url": "https://github.com/mem0ai/mem0/pull/5571",
"updated_at": "2026-06-30T05:20:27Z",
"head": {"sha": "d0b1cfa", "ref": "funasr-audio-helper"},
"base": {"ref": "main"},
"user": {"login": "anneheartrecord"},
}
if url == "https://api.github.com/repos/mem0ai/mem0/commits/d0b1cfa/status":
return {
"state": "failure",
"statuses": [
{
"context": "Vercel",
"state": "failure",
"target_url": "https://vercel.com/git/authorize?team=Mem0&slug=mem0&pullRequest=5571",
}
],
}
if url == "https://api.github.com/repos/mem0ai/mem0/commits/d0b1cfa/check-runs?per_page=100":
return {"total_count": 0, "check_runs": []}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["mem0ai/mem0#5571"])
integration = metrics["integrations"][0]
assert integration["next_action"] == "wait for preview authorization"
assert integration["checks"]["failed_check_runs"] == [
{
"name": "Vercel",
"conclusion": "failure",
"url": "https://vercel.com/git/authorize?team=Mem0&slug=mem0&pullRequest=5571",
}
]
def test_recommend_integration_action_treats_blocked_unknown_checks_as_review_gate():
module = load_growth_metrics_module()
action = module.recommend_integration_action(
{"state": "open", "draft": False, "mergeable_state": "blocked"},
{"state": "unknown", "failed_check_runs": [], "pending_check_runs": []},
)
assert action == "review gate"
def test_recommend_integration_action_waits_for_maintainer_after_assisted_blocked_review_gate():
module = load_growth_metrics_module()
action = module.recommend_integration_action(
{"state": "open", "draft": False, "mergeable_state": "blocked"},
{"state": "success", "failed_check_runs": [], "pending_check_runs": []},
known_assisted_review_reason="review evidence already posted; avoid duplicate pings",
)
assert action == "wait for maintainer review"
def test_recommend_integration_action_treats_clean_unknown_checks_as_request_review():
module = load_growth_metrics_module()
action = module.recommend_integration_action(
{"state": "open", "draft": False, "mergeable_state": "clean"},
{"state": "unknown", "failed_check_runs": [], "pending_check_runs": []},
)
assert action == "request review"
def test_recommend_integration_action_treats_dirty_merge_state_as_conflict_resolution():
module = load_growth_metrics_module()
action = module.recommend_integration_action(
{"state": "open", "draft": False, "mergeable_state": "dirty"},
{"state": "unknown", "failed_check_runs": [], "pending_check_runs": []},
)
assert action == "resolve conflicts"
def test_recommend_integration_action_prioritizes_dirty_merge_state_over_failed_checks():
module = load_growth_metrics_module()
action = module.recommend_integration_action(
{"state": "open", "draft": False, "mergeable_state": "dirty"},
{
"state": "failure",
"failed_check_runs": [{"name": "ci", "conclusion": "failure"}],
"pending_check_runs": [],
},
)
assert action == "resolve conflicts"
def test_recommend_integration_action_prioritizes_dirty_merge_state_over_pending_checks():
module = load_growth_metrics_module()
action = module.recommend_integration_action(
{"state": "open", "draft": False, "mergeable_state": "dirty"},
{
"state": "pending",
"failed_check_runs": [],
"pending_check_runs": [{"name": "ci", "status": "queued"}],
},
)
assert action == "resolve conflicts"
def test_collect_integration_metrics_treats_empty_pending_status_as_review_gate(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/sgl-project/sglang-omni/pulls/898":
return {
"number": 898,
"title": "Add Fun-ASR serving support",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "blocked",
"html_url": "https://github.com/sgl-project/sglang-omni/pull/898",
"updated_at": "2026-06-30T04:48:08Z",
"head": {"sha": "c081a79", "ref": "funasr-serving"},
"base": {"ref": "main"},
"user": {"login": "PoTaTo-Mika"},
}
if url == "https://api.github.com/repos/sgl-project/sglang-omni/commits/c081a79/status":
return {"state": "pending", "statuses": []}
if url == "https://api.github.com/repos/sgl-project/sglang-omni/commits/c081a79/check-runs?per_page=100":
return {"total_count": 0, "check_runs": []}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_integration_metrics(["sgl-project/sglang-omni#898"])
integration = metrics["integrations"][0]
assert integration["checks"]["state"] == "unknown"
assert integration["checks"]["pending_check_runs"] == []
assert integration["next_action"] == "wait for contributor conflict resolution"
def test_format_integration_markdown_includes_update_age_and_next_action():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-07-02T00:00:00+00:00",
"integrations": [
{
"pr": "huggingface/transformers#46180",
"html_url": "https://github.com/huggingface/transformers/pull/46180",
"state": "open",
"mergeable_state": "blocked",
"repo_stars": 155_000,
"repo_forks": 31_000,
"updated_at": "2026-06-29T23:45:57Z",
"updated_age_days": 2,
"next_action": "fix checks",
"checks": {"state": "failure", "failed_check_runs": [], "pending_check_runs": []},
}
],
}
output = module.format_integration_markdown(metrics)
assert "| Pull request | Stars | Forks | State | Mergeable | Checks | Failed | Pending | Age | Action | Updated |" in output
assert (
"| [huggingface/transformers#46180](https://github.com/huggingface/transformers/pull/46180) | "
"155,000 | 31,000 | open | blocked | failure | 0 | 0 | 2d | fix checks | `2026-06-29T23:45:57Z` |"
) in output
def test_format_integration_markdown_includes_known_external_failure_reason():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-07-02T00:00:00+00:00",
"integrations": [
{
"pr": "huggingface/transformers#46180",
"html_url": "https://github.com/huggingface/transformers/pull/46180",
"state": "open",
"mergeable_state": "blocked",
"repo_stars": 162_000,
"repo_forks": 33_000,
"updated_at": "2026-06-30T04:24:15Z",
"updated_age_days": 0,
"next_action": "request rerun",
"known_external_failure_reason": (
"LightOnOCR shared hub-cache read-only failure; PR CI status is the aggregate failure"
),
"checks": {
"state": "failure",
"failed_check_runs": [
{
"name": "pr-ci / tests_processors / tests_processors [shard 3/8]",
"conclusion": "failure",
"url": "https://github.com/huggingface/transformers/actions/runs/28419525291/job/84209871338",
}
],
"pending_check_runs": [],
},
}
],
}
output = module.format_integration_markdown(metrics)
assert (
"- Known external failure: LightOnOCR shared hub-cache read-only failure; "
"PR CI status is the aggregate failure"
) in output
def test_format_integration_markdown_marks_missing_exposure_metrics_as_unavailable():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-07-02T00:00:00+00:00",
"integrations": [
{
"pr": "deleted-org/deleted-repo#1",
"html_url": "https://github.com/deleted-org/deleted-repo/pull/1",
"state": "open",
"mergeable_state": "unknown",
"updated_at": "2026-06-29T23:45:57Z",
"updated_age_days": None,
"next_action": "inspect",
"checks": {"state": "unknown", "failed_check_runs": [], "pending_check_runs": []},
}
],
}
output = module.format_integration_markdown(metrics)
assert (
"| [deleted-org/deleted-repo#1](https://github.com/deleted-org/deleted-repo/pull/1) | "
"n/a | n/a | open | unknown | unknown | 0 | 0 | n/a | inspect | `2026-06-29T23:45:57Z` |"
) in output
def test_format_integration_markdown_lists_high_exposure_priorities_by_stars():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-07-02T00:00:00+00:00",
"integrations": [
{
"pr": "ray-project/ray#64053",
"html_url": "https://github.com/ray-project/ray/pull/64053",
"state": "open",
"mergeable_state": "blocked",
"repo_stars": 43_000,
"repo_forks": 7_700,
"updated_at": "2026-06-30T07:02:58Z",
"updated_age_days": 0,
"next_action": "review gate",
"checks": {"state": "success", "failed_check_runs": [], "pending_check_runs": []},
},
{
"pr": "huggingface/transformers#46180",
"html_url": "https://github.com/huggingface/transformers/pull/46180",
"state": "open",
"mergeable_state": "blocked",
"repo_stars": 162_000,
"repo_forks": 33_000,
"updated_at": "2026-06-30T04:24:15Z",
"updated_age_days": 0,
"next_action": "request rerun",
"checks": {"state": "failure", "failed_check_runs": [], "pending_check_runs": []},
},
{
"pr": "deleted-org/deleted-repo#1",
"html_url": "https://github.com/deleted-org/deleted-repo/pull/1",
"state": "open",
"mergeable_state": "unknown",
"updated_at": "2026-06-29T23:45:57Z",
"updated_age_days": None,
"next_action": "inspect",
"checks": {"state": "unknown", "failed_check_runs": [], "pending_check_runs": []},
},
],
}
output = module.format_integration_markdown(metrics)
assert "## High-exposure priorities" in output
transformers = "- [huggingface/transformers#46180](https://github.com/huggingface/transformers/pull/46180): 162,000 stars, request rerun"
ray = "- [ray-project/ray#64053](https://github.com/ray-project/ray/pull/64053): 43,000 stars, review gate"
assert transformers in output
assert ray in output
assert output.index(transformers) < output.index(ray)
assert "deleted-org/deleted-repo#1" not in output.split("## High-exposure priorities", 1)[1].split("## Failed or pending checks", 1)[0]
def test_format_integration_markdown_lists_active_operator_queue():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-07-02T00:00:00+00:00",
"integrations": [
{
"pr": "getpaseo/paseo#1634",
"html_url": "https://github.com/getpaseo/paseo/pull/1634",
"state": "open",
"mergeable_state": "dirty",
"repo_stars": 9_500,
"repo_forks": 900,
"updated_at": "2026-06-30T09:47:12Z",
"updated_age_days": 0,
"next_action": "resolve conflicts",
"checks": {"state": "unknown", "failed_check_runs": [], "pending_check_runs": []},
},
{
"pr": "huggingface/optimum-intel#1801",
"html_url": "https://github.com/huggingface/optimum-intel/pull/1801",
"state": "open",
"mergeable_state": "unstable",
"repo_stars": 600,
"repo_forks": 240,
"updated_at": "2026-06-30T04:20:02Z",
"updated_age_days": 0,
"next_action": "review gate",
"checks": {"state": "unknown", "failed_check_runs": [], "pending_check_runs": []},
},
{
"pr": "livekit/agents#6176",
"html_url": "https://github.com/livekit/agents/pull/6176",
"state": "open",
"mergeable_state": "blocked",
"repo_stars": 11_000,
"repo_forks": 3_200,
"updated_at": "2026-06-30T10:55:17Z",
"updated_age_days": 0,
"next_action": "wait for maintainer review",
"checks": {"state": "success", "failed_check_runs": [], "pending_check_runs": []},
},
{
"pr": "activepieces/activepieces#13985",
"html_url": "https://github.com/activepieces/activepieces/pull/13985",
"state": "open",
"mergeable_state": "blocked",
"repo_stars": 23_000,
"repo_forks": 3_800,
"updated_at": "2026-06-30T08:46:39Z",
"updated_age_days": 0,
"next_action": "resolve CLA",
"checks": {"state": "pending", "failed_check_runs": [], "pending_check_runs": []},
},
{
"pr": "huggingface/optimum-intel#1801",
"html_url": "https://github.com/huggingface/optimum-intel/pull/1801",
"state": "open",
"mergeable_state": "unstable",
"repo_stars": 18_000,
"repo_forks": 2_800,
"updated_at": "2026-06-30T04:20:02Z",
"updated_age_days": 0,
"next_action": "wait for maintainer preliminary PR",
"checks": {"state": "failure", "failed_check_runs": [], "pending_check_runs": []},
},
],
}
output = module.format_integration_markdown(metrics)
active_queue = output.split("## Active operator queue", 1)[1].split("## Failed or pending checks", 1)[0]
assert "- [getpaseo/paseo#1634](https://github.com/getpaseo/paseo/pull/1634): resolve conflicts" in active_queue
assert (
"- [huggingface/optimum-intel#1801](https://github.com/huggingface/optimum-intel/pull/1801): review gate"
in active_queue
)
assert "livekit/agents#6176" not in active_queue
assert "activepieces/activepieces#13985" not in active_queue
assert "wait for maintainer preliminary PR" not in active_queue
def test_format_integration_markdown_lists_manual_handoff_gates():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-07-02T00:00:00+00:00",
"integrations": [
{
"pr": "punkpeye/awesome-mcp-servers#7153",
"html_url": "https://github.com/punkpeye/awesome-mcp-servers/pull/7153",
"state": "open",
"mergeable_state": "clean",
"repo_stars": 90_000,
"repo_forks": 12_000,
"updated_at": "2026-06-16T04:21:55Z",
"updated_age_days": 16,
"next_action": "submit Glama",
"known_review_gate_reason": "Glama listing and score badge required before review",
"checks": {"state": "success", "failed_check_runs": [], "pending_check_runs": []},
},
{
"pr": "huggingface/optimum-intel#1801",
"html_url": "https://github.com/huggingface/optimum-intel/pull/1801",
"state": "open",
"mergeable_state": "unstable",
"repo_stars": 600,
"repo_forks": 240,
"updated_at": "2026-06-30T04:20:02Z",
"updated_age_days": 0,
"next_action": "review gate",
"checks": {"state": "unknown", "failed_check_runs": [], "pending_check_runs": []},
},
],
}
output = module.format_integration_markdown(metrics)
active_queue = output.split("## Active operator queue", 1)[1].split("## Manual handoff gates", 1)[0]
manual_gates = output.split("## Manual handoff gates", 1)[1].split("## Manual review gates", 1)[0]
assert "punkpeye/awesome-mcp-servers#7153" not in active_queue
assert (
"- [punkpeye/awesome-mcp-servers#7153](https://github.com/punkpeye/awesome-mcp-servers/pull/7153): "
"submit Glama; Glama listing and score badge required before review"
) in manual_gates
assert "huggingface/optimum-intel#1801" not in manual_gates
def test_format_integration_markdown_lists_known_review_gates():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-07-02T00:00:00+00:00",
"integrations": [
{
"pr": "punkpeye/awesome-mcp-servers#7153",
"html_url": "https://github.com/punkpeye/awesome-mcp-servers/pull/7153",
"state": "open",
"mergeable_state": "clean",
"repo_stars": 90_000,
"repo_forks": 12_000,
"updated_at": "2026-06-16T04:21:55Z",
"updated_age_days": 14,
"next_action": "submit Glama",
"known_review_gate_reason": "Glama listing and score badge required before review",
"checks": {"state": "success", "failed_check_runs": [], "pending_check_runs": []},
}
],
}
output = module.format_integration_markdown(metrics)
assert "## Manual review gates" in output
assert (
"- [punkpeye/awesome-mcp-servers#7153](https://github.com/punkpeye/awesome-mcp-servers/pull/7153): "
"Glama listing and score badge required before review"
) in output
def test_format_integration_markdown_lists_assisted_review_waits():
module = load_growth_metrics_module()
metrics = {
"collected_at_utc": "2026-07-02T00:00:00+00:00",
"integrations": [
{
"pr": "lukasmasuch/best-of-ml-python#455",
"html_url": "https://github.com/lukasmasuch/best-of-ml-python/pull/455",
"state": "open",
"mergeable_state": "clean",
"repo_stars": 23_652,
"repo_forks": 3_800,
"updated_at": "2026-06-30T09:16:59Z",
"updated_age_days": 0,
"next_action": "wait for maintainer review",
"known_assisted_review_reason": "review evidence already posted; avoid duplicate pings",
"checks": {"state": "success", "failed_check_runs": [], "pending_check_runs": []},
}
],
}
output = module.format_integration_markdown(metrics)
assert "## Assisted review waits" in output
assert (
"- [lukasmasuch/best-of-ml-python#455](https://github.com/lukasmasuch/best-of-ml-python/pull/455): "
"review evidence already posted; avoid duplicate pings"
) in output
def test_collect_issue_metrics_filters_pull_requests_and_assigns_waiting_on(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/modelscope/FunASR/issues?state=open&per_page=100":
return [
{
"number": 3038,
"title": "realtime websocket falls behind with two clients",
"html_url": "https://github.com/modelscope/FunASR/issues/3038",
"updated_at": "2026-06-29T19:22:38Z",
"comments": 4,
"user": {"login": "liujixingit"},
"labels": [{"name": "bug"}, {"name": "needs feedback"}],
},
{
"number": 3034,
"title": "Fun-ASR-Nano on Ascend NPU",
"html_url": "https://github.com/modelscope/FunASR/issues/3034",
"updated_at": "2026-06-29T19:49:07Z",
"comments": 3,
"user": {"login": "pubulichen"},
"labels": [{"name": "help wanted"}, {"name": "ready for PR"}],
},
{
"number": 3057,
"title": "open pull request should be ignored",
"pull_request": {"url": "https://api.github.com/repos/modelscope/FunASR/pulls/3057"},
"labels": [],
},
]
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
metrics = module.collect_issue_metrics(["modelscope/FunASR"])
repository = metrics["repositories"][0]
assert repository["repo"] == "modelscope/FunASR"
assert repository["open_issue_count"] == 2
assert [issue["number"] for issue in repository["open_issues"]] == [3038, 3034]
assert repository["open_issues"][0]["labels"] == ["bug", "needs feedback"]
assert repository["open_issues"][0]["waiting_on"] == "reporter"
assert repository["open_issues"][1]["waiting_on"] == "contributor"
def test_main_outputs_issues_json(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/modelscope/FunASR/issues?state=open&per_page=100":
return [
{
"number": 2959,
"title": "postprocess hotwords",
"html_url": "https://github.com/modelscope/FunASR/issues/2959",
"updated_at": "2026-06-29T19:46:28Z",
"comments": 2,
"user": {"login": "HaujetZhao"},
"labels": [{"name": "good first issue"}, {"name": "ready for PR"}],
}
]
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
monkeypatch.setattr(
sys,
"argv",
[
"collect_growth_metrics.py",
"--issues",
"--repos",
"modelscope/FunASR",
"--format",
"json",
],
)
with redirect_stdout(io.StringIO()) as stdout:
assert module.main() == 0
payload = json.loads(stdout.getvalue())
assert payload["repositories"][0]["open_issues"][0]["number"] == 2959
assert payload["repositories"][0]["open_issues"][0]["waiting_on"] == "contributor"
def test_main_outputs_ecosystem_json(monkeypatch):
module = load_growth_metrics_module()
repo_stars = {
"modelscope/FunASR": 18714,
"FunAudioLLM/Fun-ASR": 1318,
"FunAudioLLM/SenseVoice": 8718,
"modelscope/FunClip": 5872,
}
def fake_fetch_json(url, headers=None):
if url.endswith("/pulls?state=open&per_page=100"):
return []
if url.startswith("https://api.github.com/repos/"):
repo = url.removeprefix("https://api.github.com/repos/")
return {
"stargazers_count": repo_stars[repo],
"forks_count": 0,
"subscribers_count": 0,
"open_issues_count": 0,
"default_branch": "main",
"pushed_at": "2026-06-30T00:00:00Z",
"html_url": f"https://github.com/{repo}",
}
if url == "https://pypi.org/pypi/funasr/json":
return {"info": {"version": "1.3.14", "summary": "FunASR", "project_url": "https://pypi.org/project/funasr/"}}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
monkeypatch.setattr(
sys,
"argv",
[
"collect_growth_metrics.py",
"--ecosystem",
"--format",
"json",
"--baseline-stars",
"31224",
"--target-additional-stars",
"20000",
],
)
with redirect_stdout(io.StringIO()) as stdout:
assert module.main() == 0
payload = json.loads(stdout.getvalue())
assert payload["ecosystem"]["total_stars"] == 34622
assert payload["ecosystem"]["remaining_to_target"] == 16602
def test_main_outputs_integrations_json(monkeypatch):
module = load_growth_metrics_module()
def fake_fetch_json(url, headers=None):
if url == "https://api.github.com/repos/ray-project/ray/pulls/64053":
return {
"number": 64053,
"title": "docs(serve): add FunASR ASR integration example",
"state": "open",
"draft": False,
"mergeable": True,
"mergeable_state": "clean",
"html_url": "https://github.com/ray-project/ray/pull/64053",
"updated_at": "2026-06-30T01:53:52Z",
"head": {"sha": "780eb4c", "ref": "issue-64052"},
"base": {
"ref": "master",
"repo": {
"stargazers_count": 36_000,
"forks_count": 6_800,
},
},
"user": {"login": "nh-atuan"},
}
if url == "https://api.github.com/repos/ray-project/ray/commits/780eb4c/status":
return {"state": "success", "statuses": [{"context": "buildkite/microcheck", "state": "success"}]}
if url == "https://api.github.com/repos/ray-project/ray/commits/780eb4c/check-runs?per_page=100":
return {
"total_count": 1,
"check_runs": [
{"name": "disable-automerge", "status": "completed", "conclusion": "success"},
],
}
raise AssertionError(f"unexpected URL: {url}")
monkeypatch.setattr(module, "fetch_json", fake_fetch_json)
monkeypatch.setattr(
sys,
"argv",
[
"collect_growth_metrics.py",
"--integrations",
"--integration-prs",
"ray-project/ray#64053",
"--format",
"json",
],
)
with redirect_stdout(io.StringIO()) as stdout:
assert module.main() == 0
payload = json.loads(stdout.getvalue())
integration = payload["integrations"][0]
assert integration["pr"] == "ray-project/ray#64053"
assert integration["repo_stars"] == 36_000
assert integration["repo_forks"] == 6_800
assert integration["checks"]["state"] == "success"
assert integration["next_action"] == "wait for maintainer review"
assert integration["known_assisted_review_reason"] == "review evidence already posted; avoid duplicate pings"