TypeScript 50.6%
JavaScript 46.1%
SQL 2.1%
Shell 0.6%
CSS 0.5%
1#!/bin/zsh2# Test de fumée API bout en bout (serveur local déjà démarré) : usage scripts/smoke.sh http://127.0.0.1:8460 [db]3set -u4B=${1:-http://127.0.0.1:8460}; DB=${2:-toveli}; ORIGIN=${3:-http://localhost:3000}; HOSTH=${ORIGIN#*://}5H=(-H "content-type: application/json" -H "Origin: $ORIGIN" -H "Host: $HOSTH")6G=(-H "Host: $HOSTH")7CJ=$(mktemp)8echo "--- request-code"; curl -s -X POST $B/api/auth/request-code "${H[@]}" -d '{"email":"Simon.Test@exemple.ca"}'; echo9CODE=$(tail -1 data/outbox/mails.jsonl | python3 -c "import json,sys,re;m=json.loads(sys.stdin.readline());print(re.search(r'\b(\d{6})\b',m['text']).group(1))"); CID=$(psql "${PSQL_URL:-$DB}" -tA -c "select id from otp_challenges order by created_at desc limit 1"); echo "code=$CODE challenge=$CID"10echo "--- verify sans conditions (attendu 409)"; curl -s -o /dev/null -w "%{http_code}\n" -X POST $B/api/auth/verify-code "${H[@]}" -d "{\"challengeId\":\"$CID\",\"code\":\"$CODE\",\"acceptTerms\":false}"11echo "--- verify avec conditions"; curl -s -c $CJ -X POST $B/api/auth/verify-code "${H[@]}" -d "{\"challengeId\":\"$CID\",\"code\":\"$CODE\",\"acceptTerms\":true}"; echo12echo "--- réutilisation (attendu 400)"; curl -s -o /dev/null -w "%{http_code}\n" -X POST $B/api/auth/verify-code "${H[@]}" -d "{\"challengeId\":\"$CID\",\"code\":\"$CODE\",\"acceptTerms\":true}"13echo "--- me"; curl -s -b $CJ "${G[@]}" $B/api/me | cut -c1-160; echo14echo "--- create listing"; LID=$(curl -s -b $CJ -X POST $B/api/me/listings "${H[@]}" -d '{"listingType":"RENT","propertyType":"appartement","address":"1225 Rue Saint-Marc","city":"Montréal","lat":45.4922,"lng":-73.5789,"bedrooms":1,"bathrooms":1,"areaSqft":650,"title":"Grand 3½ lumineux près du métro","description":"Logement rénové, très lumineux, planchers de bois, à deux pas du métro Guy-Concordia et des commerces. Chauffage inclus.","priceAmount":1450,"availabilityDate":"now"}' | python3 -c "import json,sys;print(json.load(sys.stdin)['listing']['id'])"); echo "listing=$LID"15python3 -c "16import struct,zlib17def png(w,h):18 raw=b''.join(b'\x00'+bytes([200,30,60]*w) for _ in range(h))19 def chunk(t,d): return struct.pack('>I',len(d))+t+d+struct.pack('>I',zlib.crc32(t+d)&0xffffffff)20 return b'\x89PNG\r\n\x1a\n'+chunk(b'IHDR',struct.pack('>IIBBBBB',w,h,8,2,0,0,0))+chunk(b'IDAT',zlib.compress(raw))+chunk(b'IEND',b'')21open('/tmp/t.png','wb').write(png(400,300))"22echo "--- upload png"; UP=$(curl -s -b $CJ -X POST $B/api/listings/$LID/media/presign "${H[@]}" -d '{"fileName":"t.png","mimeType":"image/png"}' | python3 -c "import json,sys;print(json.load(sys.stdin)['uploadUrl'])"); curl -s -b $CJ -X PUT "$B$UP" -H "content-type: image/png" "${H[@]:1}" --data-binary @/tmp/t.png; echo23echo "--- upload fichier non image (attendu 422)"; UP=$(curl -s -b $CJ -X POST $B/api/listings/$LID/media/presign "${H[@]}" -d '{}' | python3 -c "import json,sys;print(json.load(sys.stdin)['uploadUrl'])"); printf 'MZ not an image at all........' | curl -s -b $CJ -X PUT "$B$UP" -H "content-type: image/png" "${H[@]:1}" --data-binary @-; echo24echo "--- publish sans attestation (attendu 422)"; curl -s -b $CJ -X POST $B/api/listings/$LID/publish "${H[@]}" | cut -c1-200; echo25echo "--- attest + publish"; curl -s -b $CJ -X POST $B/api/listings/$LID/attest "${H[@]}" >/dev/null; curl -s -b $CJ -X POST $B/api/listings/$LID/publish "${H[@]}"; echo26SLUG=$(psql "${PSQL_URL:-$DB}" -tA -c "select slug from listings where id='$LID'"); echo "--- page publique toveli:$SLUG"; curl -s -o /tmp/dp.html -w "%{http_code} %{size_download}o\n" "$B/annonce/toveli%3A$SLUG"; grep -o "Publiée par le propriétaire\|Contacter\|Demander une visite\|En bref\|Budget mensuel" /tmp/dp.html | sort | uniq -c27echo "--- recherche unifiée"; curl -s "$B/api/search?marche=location&ville=Montr%C3%A9al&limit=3" | python3 -c "import json,sys;d=json.load(sys.stdin);print(d['total'], [i['uid'] for i in d['items']][:3])"28echo "--- second utilisateur : contact + visite"; curl -s -X POST $B/api/auth/request-code "${H[@]}" -d '{"email":"acheteur@exemple.ca"}' >/dev/null; CODE2=$(tail -1 data/outbox/mails.jsonl | python3 -c "import json,sys,re;m=json.loads(sys.stdin.readline());print(re.search(r'\b(\d{6})\b',m['text']).group(1))"); CID2=$(psql "${PSQL_URL:-$DB}" -tA -c "select id from otp_challenges order by created_at desc limit 1"); CJ2=$(mktemp); curl -s -c $CJ2 -o /dev/null -X POST $B/api/auth/verify-code "${H[@]}" -d "{\"challengeId\":\"$CID2\",\"code\":\"$CODE2\",\"acceptTerms\":true}"29curl -s -b $CJ2 -X POST $B/api/listings/$LID/inquiries "${H[@]}" -d '{"body":"Bonjour, le logement est-il encore disponible ? Mon numéro : 514 555 0100"}'; echo30SLOT=$(python3 -c "import datetime;print((datetime.datetime.now(datetime.timezone.utc)+datetime.timedelta(days=2)).isoformat())"); curl -s -b $CJ2 -X POST $B/api/listings/$LID/viewings "${H[@]}" -d "{\"slots\":[\"$SLOT\"],\"note\":\"Je peux passer en soirée.\"}"; echo31echo "--- propriétaire voit la demande (téléphone masqué ?)"; curl -s -b $CJ "${G[@]}" $B/api/me/inquiries | python3 -c "import json,sys;d=json.load(sys.stdin);print(len(d['inquiries']),'conversations,',len(d['viewings']),'visites;', d['inquiries'][0]['lastBody'] if d['inquiries'] else '')"32echo "--- intrus (attendu 404)"; IQ=$(psql "${PSQL_URL:-$DB}" -tA -c "select id from inquiries order by created_at desc limit 1"); curl -s -o /dev/null -w "%{http_code}\n" "${G[@]}" $B/api/inquiries/$IQ/messages33echo "--- pages"; for u in /membre /membre/annonces /membre/annonces/$LID /membre/messages /membre/visites /membre/profil /membre/securite /membre/alertes /membre/favoris /connexion /admin "/recherche?marche=location&ville=Montr%C3%A9al"; do printf "%s → " "$u"; curl -s -b $CJ "${G[@]}" -o /dev/null -w "%{http_code}\n" "$B$u"; done34echo "--- courriels simulés"; psql "${PSQL_URL:-$DB}" -tA -c "select template, status, count(*) from email_deliveries group by 1,2 order by 1"35