Spaces:
Sleeping
Sleeping
Download test_api.py from Srinu007/chromeleon1: direct link, hf CLI and curl.
- Browser
- Download file 8.67 kB
-
https://huggingface.co/spaces/Srinu007/chromeleon1/resolve/main/test_api.py
- Command line
-
hf download hf://spaces/Srinu007/chromeleon1/test_api.py
-
curl -L -o test_api.py https://huggingface.co/spaces/Srinu007/chromeleon1/resolve/main/test_api.py
8.67 kB
| """tests/test_api.py — pytest suite (no GROQ_API_KEY required to run)""" | |
| import os, math, pytest | |
| from unittest.mock import MagicMock | |
| os.environ.pop("GROQ_API_KEY", None) # ensure clean state | |
| from fastapi.testclient import TestClient | |
| def client(): | |
| from app import app, DB, STAGE_MAP | |
| stub = [ | |
| {"i":"1","p":"ACARBOSE","s":"ACA_SEQ_01","d":"2025-01-03","sc":"RM", | |
| "it":"Unknown","st":"Finished","sw":501.0,"t":"ASSAY","b":"BATCH001", | |
| "ar":"AR001","in":"INSTR-01","co":"COL-01","d1":25.0,"wl":"210 nm", | |
| "ft":"Isocratic","th":25.0,"ct":35.0,"rp":106.0,"pp":102.0, | |
| "pa":"311000.0","pr":"17.6","pas":"1.19","pp2":"3900","cn":"Acarbose","sw1":"502.41","pn":"Acarbose"}, | |
| {"i":"2","p":"METFORMIN","s":"MET_SEQ_01","d":"2025-03-15","sc":"FP", | |
| "it":"CalibrationStandard","st":"Finished","sw":200.0,"t":"DISSOLUTION", | |
| "b":"BATCH002","ar":"AR002","in":"INSTR-02","co":"COL-02","d1":100.0, | |
| "wl":"254 nm","ft":"Gradient","th":37.0,"ct":40.0,"rp":200.0,"pp":198.0, | |
| "pa":"150000.0","pr":"5.3","pas":"1.05","pp2":"5200","cn":"Metformin HCl","sw1":"100.0","pn":"Metformin"}, | |
| {"i":"3","p":"ACARBOSE","s":"ACA_SEQ_01","d":"2025-06-20","sc":"RM", | |
| "it":"Blank","st":"Finished","sw":None,"t":"ASSAY","b":"BATCH001", | |
| "ar":"AR001","in":"INSTR-01","co":"COL-01","d1":1.0,"wl":"210 nm", | |
| "ft":"Isocratic","th":25.0,"ct":35.0,"rp":104.0,"pp":101.0, | |
| "pa":None,"pr":None,"pas":None,"pp2":None,"cn":None,"sw1":None,"pn":None}, | |
| ] | |
| areas=[float(r["pa"]) for r in stub if r.get("pa")] | |
| pv=[float(r["rp"]) for r in stub if r.get("rp")] | |
| cv=[float(r["ct"]) for r in stub if r.get("ct") is not None] | |
| def avg(a): return sum(a)/len(a) if a else 0.0 | |
| def std(a): | |
| if len(a)<2: return 0.0 | |
| m=avg(a); return math.sqrt(sum((x-m)**2 for x in a)/len(a)) | |
| DB.records=stub | |
| DB.agg={"total":3,"finished":3, | |
| "area_mean":avg(areas),"area_std":std(areas),"area_n":len(areas), | |
| "pres_mean":avg(pv),"pres_std":std(pv),"pres_n":len(pv), | |
| "ct_mean":avg(cv),"ct_std":std(cv),"ct_n":len(cv), | |
| "stage_cnt":{"Raw Material":2,"Finished Product":1}, | |
| "it_cnt":{"Unknown":1,"CalibrationStandard":1,"Blank":1}, | |
| "instr_cnt":{"INSTR-01":2,"INSTR-02":1},"wl_cnt":{"210 nm":2,"254 nm":1}, | |
| "prod_cnt":{"ACARBOSE":2,"METFORMIN":1},"ft_cnt":{"Isocratic":2,"Gradient":1}, | |
| "thermo_cnt":{"25.0":2,"37.0":1},"ct_cnt":{"35.0":2,"40.0":1}, | |
| "daily":[ | |
| {"d":"2025-01-03","n":1,"a":311000.0,"p":106.0,"c":35.0,"r":17.6,"as":1.19}, | |
| {"d":"2025-03-15","n":1,"a":150000.0,"p":200.0,"c":40.0,"r":5.3,"as":1.05}, | |
| {"d":"2025-06-20","n":1,"a":None,"p":104.0,"c":35.0,"r":None,"as":None}, | |
| ], | |
| "area_sample":areas,"sa_stats":{"Raw Material":{"mean":311000.0,"std":0.0,"n":1}},"it_stats":{}} | |
| DB.meta={"total":3,"sequences":["ACA_SEQ_01","MET_SEQ_01"],"products":["ACARBOSE","METFORMIN"], | |
| "instruments":["INSTR-01","INSTR-02"],"columns":["COL-01","COL-02"], | |
| "inj_types":["Blank","CalibrationStandard","Unknown"],"stage_cats":list(STAGE_MAP.values()), | |
| "tests":["ASSAY","DISSOLUTION"],"wavelengths":["210 nm","254 nm"],"flow_types":["Gradient","Isocratic"]} | |
| DB.loaded=True | |
| return TestClient(app) | |
| class TestHealth: | |
| def test_ok(self, client): | |
| r=client.get("/api/health"); assert r.status_code==200 | |
| b=r.json(); assert b["status"]=="ok"; assert b["records"]==3 | |
| assert b["port"]==7860; assert "ai_enabled" in b | |
| def test_meta(self, client): | |
| r=client.get("/api/meta"); assert r.status_code==200 | |
| assert "ACARBOSE" in r.json()["products"] | |
| def test_agg(self, client): | |
| r=client.get("/api/aggregate"); assert r.status_code==200; assert r.json()["total"]==3 | |
| class TestInjections: | |
| def test_list(self, client): assert client.get("/api/injections").json()["total"]==3 | |
| def test_page(self, client): | |
| r=client.get("/api/injections?per_page=2"); assert len(r.json()["data"])==2 | |
| def test_product(self, client): assert client.get("/api/injections?product=ACARBOSE").json()["total"]==2 | |
| def test_inj_type(self, client): assert client.get("/api/injections?inj_type=Blank").json()["total"]==1 | |
| def test_stage(self, client): assert client.get("/api/injections?stage=Finished+Product").json()["total"]==1 | |
| def test_dates(self, client): assert client.get("/api/injections?date_from=2025-02-01").json()["total"]==2 | |
| def test_pressure(self, client): assert client.get("/api/injections?pres_min=150&pres_max=300").json()["total"]==1 | |
| def test_instrument(self, client): assert client.get("/api/injections?instrument=INSTR-02").json()["total"]==1 | |
| def test_wavelength(self, client): assert client.get("/api/injections?wavelength=254+nm").json()["total"]==1 | |
| def test_fulltext(self, client): assert client.get("/api/injections?q=BATCH002").json()["total"]==1 | |
| def test_stage_label(self, client): | |
| for rec in client.get("/api/injections").json()["data"]: assert "stage_label" in rec | |
| class TestSingle: | |
| def test_found(self, client): | |
| r=client.get("/api/injections/1"); assert r.status_code==200 | |
| assert r.json()["stage_label"]=="Raw Material" | |
| def test_missing(self, client): assert client.get("/api/injections/99999").status_code==404 | |
| class TestTrends: | |
| def test_count(self, client): assert client.get("/api/trends/daily?metric=n").json()["values"]==[1,1,1] | |
| def test_area(self, client): assert client.get("/api/trends/daily?metric=a").json()["values"][0]==311000.0 | |
| def test_date_filter(self, client): | |
| assert len(client.get("/api/trends/daily?date_from=2025-03-01").json()["labels"])==2 | |
| class TestStats: | |
| def test_summary(self, client): | |
| b=client.get("/api/stats/summary").json() | |
| assert b["total"]==3; assert b["finish_rate_pct"]==100.0 | |
| assert b["area"]["p10"]<=b["area"]["median"]<=b["area"]["p90"] | |
| class TestExport: | |
| def test_csv(self, client): | |
| r=client.get("/api/injections/export"); assert "text/csv" in r.headers["content-type"] | |
| lines=r.text.strip().split("\n"); assert lines[0].startswith("Product,Batch"); assert len(lines)==4 | |
| def test_filtered(self, client): | |
| lines=client.get("/api/injections/export?product=ACARBOSE").text.strip().split("\n"); assert len(lines)==3 | |
| class TestCompare: | |
| def test_ok(self, client): | |
| r=client.post("/api/compare",json={"by":"product","groups":["ACARBOSE","METFORMIN"]}) | |
| assert r.status_code==200; assert r.json()["groups"]["ACARBOSE"]["count"]==2 | |
| def test_too_few(self, client): assert client.post("/api/compare",json={"by":"product","groups":["A"]}).status_code==400 | |
| def test_too_many(self, client): assert client.post("/api/compare",json={"by":"product","groups":["A","B","C","D"]}).status_code==400 | |
| def test_bad_by(self, client): assert client.post("/api/compare",json={"by":"bad","groups":["A","B"]}).status_code==400 | |
| class TestAI: | |
| def test_models_no_key(self, client): | |
| # Works without any key — just lists models | |
| r=client.get("/api/ai/models"); assert r.status_code==200 | |
| b=r.json(); assert b["provider"]=="GROQ" | |
| assert any(m["id"]=="llama-3.3-70b-versatile" for m in b["models"]) | |
| assert b["enabled"]==False # no key in test env | |
| assert "note" in b | |
| def test_chat_no_key_503(self, client): | |
| # Without key → 503 with clear message | |
| r=client.post("/api/ai/chat",json={"messages":[{"role":"user","content":"hi"}]}) | |
| assert r.status_code==503 | |
| assert "GROQ_API_KEY" in r.json()["detail"] | |
| assert "console.groq.com" in r.json()["detail"] | |
| def test_chat_with_mock(self, client): | |
| import app as app_mod | |
| mock_resp=MagicMock() | |
| mock_resp.choices=[MagicMock()] | |
| mock_resp.choices[0].message.content="RSD 12% is within acceptable limits." | |
| mock_resp.choices[0].finish_reason="stop" | |
| mock_resp.model="llama-3.3-70b-versatile" | |
| mock_resp.usage.prompt_tokens=50; mock_resp.usage.completion_tokens=30; mock_resp.usage.total_tokens=80 | |
| mock_client=MagicMock() | |
| mock_client.chat.completions.create.return_value=mock_resp | |
| original=app_mod._get_groq_client | |
| app_mod._get_groq_client=lambda: mock_client | |
| r=client.post("/api/ai/chat",json={"messages":[{"role":"user","content":"Is RSD ok?"}]}) | |
| app_mod._get_groq_client=original | |
| assert r.status_code==200 | |
| assert "RSD" in r.json()["reply"] | |
| assert r.json()["model"]=="llama-3.3-70b-versatile" | |