feat(clone-app): add check-appstore.py iTunes search with tests
This commit is contained in:
parent
4b010a665e
commit
87acf85d6f
|
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Best-effort check whether an iOS App Store equivalent exists, via the
|
||||
public iTunes Search API. There is no reliable Android-package → App-Store-ID
|
||||
mapping, so we search by term (app title) and return the top matches."""
|
||||
import sys, json, argparse, urllib.request, urllib.parse
|
||||
|
||||
def fetch(term):
|
||||
q = urllib.parse.urlencode({"term": term, "entity": "software", "limit": 5})
|
||||
url = f"https://itunes.apple.com/search?{q}"
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
||||
with urllib.request.urlopen(req, timeout=30) as r:
|
||||
return json.loads(r.read().decode("utf-8", "replace"))
|
||||
|
||||
def shape(data):
|
||||
results = []
|
||||
for r in data.get("results", []):
|
||||
results.append({
|
||||
"name": r.get("trackName"),
|
||||
"seller": r.get("sellerName"),
|
||||
"rating": r.get("averageUserRating"),
|
||||
"rating_count": r.get("userRatingCount"),
|
||||
"price": r.get("formattedPrice"),
|
||||
"url": r.get("trackViewUrl"),
|
||||
})
|
||||
return {"found": len(results) > 0, "source": "app-store", "results": results}
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("term")
|
||||
ap.add_argument("--json-file")
|
||||
args = ap.parse_args()
|
||||
|
||||
if args.json_file:
|
||||
with open(args.json_file, encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
else:
|
||||
try:
|
||||
data = fetch(args.term)
|
||||
except Exception as e:
|
||||
print(json.dumps({"found": False, "source": "app-store",
|
||||
"results": [], "error": str(e)}, indent=2))
|
||||
sys.exit(0)
|
||||
|
||||
print(json.dumps(shape(data), indent=2))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"resultCount": 1,
|
||||
"results": [
|
||||
{
|
||||
"trackName": "Example App",
|
||||
"sellerName": "Example Studio",
|
||||
"averageUserRating": 4.5,
|
||||
"userRatingCount": 8421,
|
||||
"formattedPrice": "Free",
|
||||
"trackViewUrl": "https://apps.apple.com/app/id123456789"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python3
|
||||
import json, subprocess, sys, os
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
SCRIPT = os.path.join(HERE, "..", "skills", "clone-app", "scripts", "check-appstore.py")
|
||||
FIXTURE = os.path.join(HERE, "fixtures", "itunes-sample.json")
|
||||
|
||||
def main():
|
||||
out = subprocess.check_output(
|
||||
[sys.executable, SCRIPT, "Example App", "--json-file", FIXTURE])
|
||||
d = json.loads(out)
|
||||
fails = []
|
||||
def check(name, cond):
|
||||
print(f"{'PASS' if cond else 'FAIL'}: {name}")
|
||||
if not cond: fails.append(name)
|
||||
check("found true", d["found"] is True)
|
||||
check("source", d["source"] == "app-store")
|
||||
check("one result", len(d["results"]) == 1)
|
||||
r = d["results"][0]
|
||||
check("name", r["name"] == "Example App")
|
||||
check("seller", r["seller"] == "Example Studio")
|
||||
check("rating", abs(r["rating"] - 4.5) < 0.001)
|
||||
check("rating_count", r["rating_count"] == 8421)
|
||||
check("price", r["price"] == "Free")
|
||||
check("url", r["url"].endswith("id123456789"))
|
||||
sys.exit(1 if fails else 0)
|
||||
|
||||
main()
|
||||
Loading…
Reference in New Issue