mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-12 01:37:06 +02:00
CI GitHub Actions (build+vet+gofmt+tests+cross-compile sur push ; release 6 cibles auto sur tag v*) + tests relay_e2eauth (anti-rejeu, lockout appairage, normalisation code)
This commit is contained in:
1 parent
725a8161ce
commit
76f0d963f9
3 files changed
+168
No files matched your search
@@ -0,0 +1,25 @@
|
||||
# CI : build + vet + gofmt + tests sur chaque push/PR.
|
||||
name: ci
|
||||
on:
|
||||
push:
|
||||
branches: ["**"]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- name: gofmt
|
||||
run: test -z "$(gofmt -l .)" || (gofmt -l . && exit 1)
|
||||
- run: go vet ./...
|
||||
- run: go build ./...
|
||||
- run: go test ./...
|
||||
- name: cross-compile (toutes les cibles release)
|
||||
run: |
|
||||
for t in darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64 windows/arm64; do
|
||||
GOOS=${t%/*} GOARCH=${t#*/} CGO_ENABLED=0 go build -o /dev/null ./cmd/jean
|
||||
done
|
||||
@@ -0,0 +1,32 @@
|
||||
# Release : sur tag vX.Y.Z, compile les 6 cibles et publie la release GitHub
|
||||
# avec les binaires (mêmes noms d'assets que les releases manuelles historiques).
|
||||
name: release
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- name: build 6 cibles
|
||||
run: |
|
||||
mkdir -p dist
|
||||
for t in darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64 windows/arm64; do
|
||||
os=${t%/*}; arch=${t#*/}; ext=""; [ "$os" = windows ] && ext=".exe"
|
||||
GOOS=$os GOARCH=$arch CGO_ENABLED=0 \
|
||||
go build -trimpath -ldflags="-s -w" -o "dist/jean-$os-$arch$ext" ./cmd/jean
|
||||
done
|
||||
ls -l dist
|
||||
- name: publier la release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: dist/*
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,111 @@
|
||||
package jean
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestIsHexPub(t *testing.T) {
|
||||
ok := strings.Repeat("ab", 32) // 64 hex chars
|
||||
cases := map[string]bool{
|
||||
ok: true,
|
||||
strings.ToUpper(ok): true,
|
||||
ok[:62]: false, // trop court
|
||||
ok + "cd": false, // trop long
|
||||
strings.Repeat("zz", 32): false, // pas hexadécimal
|
||||
"": false,
|
||||
strings.Repeat("ab", 31) + "g1": false,
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := isHexPub(in); got != want {
|
||||
t.Errorf("isHexPub(%q) = %v, want %v", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashPairCodeNormalise(t *testing.T) {
|
||||
// Le code est normalisé (majuscules + trim) avant hachage : l'utilisateur
|
||||
// peut le taper en minuscules ou avec des espaces autour.
|
||||
ref := hashPairCode("ABCD-1234")
|
||||
for _, v := range []string{"abcd-1234", " ABCD-1234 ", "Abcd-1234\n"} {
|
||||
if hashPairCode(v) != ref {
|
||||
t.Errorf("hashPairCode(%q) devrait égaler hashPairCode(\"ABCD-1234\")", v)
|
||||
}
|
||||
}
|
||||
if hashPairCode("ABCD-1235") == ref {
|
||||
t.Error("codes différents → hashs différents attendus")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplayCheck(t *testing.T) {
|
||||
replayMu.Lock()
|
||||
replaySeen = map[string]time.Time{}
|
||||
replayMu.Unlock()
|
||||
|
||||
ts := time.Now().UnixMilli()
|
||||
if replayCheck("pubA", ts, "iv1") {
|
||||
t.Fatal("première vue : ne doit pas être un rejeu")
|
||||
}
|
||||
if !replayCheck("pubA", ts, "iv1") {
|
||||
t.Fatal("même (upub|ts|iv) revu : doit être détecté comme rejeu")
|
||||
}
|
||||
// Une composante différente = requête distincte, pas un rejeu.
|
||||
if replayCheck("pubA", ts, "iv2") || replayCheck("pubB", ts, "iv1") || replayCheck("pubA", ts+1, "iv1") {
|
||||
t.Error("clé différente (iv/upub/ts) ne doit pas être vue comme rejeu")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplayCheckPurge(t *testing.T) {
|
||||
replayMu.Lock()
|
||||
replaySeen = map[string]time.Time{}
|
||||
old := time.Now().Add(-3 * e2eAuthWindowMs * time.Millisecond)
|
||||
for i := 0; i < 5000; i++ {
|
||||
replaySeen[strings.Repeat("x", 8)+string(rune(i))] = old
|
||||
}
|
||||
replayMu.Unlock()
|
||||
|
||||
// Au-delà de 4096 entrées, les entrées hors fenêtre sont purgées au passage.
|
||||
replayCheck("pub", time.Now().UnixMilli(), "iv")
|
||||
replayMu.Lock()
|
||||
n := len(replaySeen)
|
||||
replayMu.Unlock()
|
||||
if n > 10 {
|
||||
t.Errorf("purge attendue des vieilles entrées, il en reste %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPairLockout(t *testing.T) {
|
||||
pairReset()
|
||||
t.Cleanup(pairReset)
|
||||
|
||||
if pairLocked() {
|
||||
t.Fatal("pas de tentative : pas de verrou")
|
||||
}
|
||||
for i := 0; i < 9; i++ {
|
||||
pairRecordFail()
|
||||
}
|
||||
if pairLocked() {
|
||||
t.Fatal("9 échecs : pas encore verrouillé (seuil = 10)")
|
||||
}
|
||||
pairRecordFail()
|
||||
if !pairLocked() {
|
||||
t.Fatal("10 échecs : appairage verrouillé attendu")
|
||||
}
|
||||
// Fenêtre de 5 min expirée → le verrou saute et le compteur repart.
|
||||
pairFailMu.Lock()
|
||||
pairLockAt = time.Now().Add(-6 * time.Minute)
|
||||
pairFailMu.Unlock()
|
||||
if pairLocked() {
|
||||
t.Fatal("fenêtre expirée : le verrou doit être levé")
|
||||
}
|
||||
if pairLocked() {
|
||||
t.Fatal("après reset du compteur, toujours déverrouillé")
|
||||
}
|
||||
}
|
||||
|
||||
func TestE2EAuthAAD(t *testing.T) {
|
||||
if string(e2eAuthAAD("pub", 42)) != "pub|42" {
|
||||
t.Errorf("AAD inattendu : %q", e2eAuthAAD("pub", 42))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user