diff --git a/.github/workflows/e2eminiooni.yml b/.github/workflows/e2eminiooni.yml new file mode 100644 index 0000000..401c231 --- /dev/null +++ b/.github/workflows/e2eminiooni.yml @@ -0,0 +1,18 @@ +# Run end-to-end testing using miniooni +name: "e2eminiooni" +on: + pull_request: + push: + branches: + - master + schedule: + - cron: "25 */8 * * *" +jobs: + test: + runs-on: "ubuntu-latest" + steps: + - uses: actions/setup-go@v1 + with: + go-version: "1.14" + - uses: actions/checkout@v2 + - run: ./E2E/miniooni.bash diff --git a/E2E/.gitignore b/E2E/.gitignore new file mode 100644 index 0000000..7a01bc8 --- /dev/null +++ b/E2E/.gitignore @@ -0,0 +1 @@ +/*.jsonl diff --git a/E2E/README.md b/E2E/README.md new file mode 100644 index 0000000..4956937 --- /dev/null +++ b/E2E/README.md @@ -0,0 +1,4 @@ +# github.com/ooni/probe-cli/E2E + +This directory is used to run end-to-end tests where we run specific +measurements, we fetch them back from the API, and check them. diff --git a/E2E/miniooni.bash b/E2E/miniooni.bash new file mode 100755 index 0000000..36b7ce5 --- /dev/null +++ b/E2E/miniooni.bash @@ -0,0 +1,18 @@ +#!/bin/bash +set -e +go build -v ./internal/cmd/miniooni +probeservices=() +probeservices+=( "https://ps1.ooni.io" ) +probeservices+=( "https://dvp6h0xblpcqp.cloudfront.net" ) +probeservices+=( "https://ams-pg-test.ooni.org" ) +for ps in ${probeservices[@]}; do + opt="-o E2E/o.jsonl --probe-services=$ps" + set -x + ./miniooni --yes $opt -i http://mail.google.com web_connectivity + ./miniooni --yes $opt tor + ./miniooni --yes $opt psiphon + set +x +done +set -x +go run ./internal/cmd/e2epostprocess -expected 9 +set +x diff --git a/internal/cmd/e2epostprocess/main.go b/internal/cmd/e2epostprocess/main.go new file mode 100644 index 0000000..06eacc4 --- /dev/null +++ b/internal/cmd/e2epostprocess/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func fatalOnError(err error) { + if err != nil { + log.Fatal(err) + } +} + +// Measurement contains a OONI measurement. +type Measurement struct { + ReportID string `json:"report_id"` + Input *string `json:"input"` + SoftwareName string `json:"software_name"` + SoftwareVersion string `json:"software_version"` +} + +func main() { + expected := flag.Int("expected", 0, "Expected number of measurement files") + flag.Parse() + if *expected <= 0 { + log.Fatal("You MUST specify `-expected N`") + } + // based off https://flaviocopes.com/go-list-files/ + var files []string + outputdir := "./E2E/" + err := filepath.Walk(outputdir, func(path string, info os.FileInfo, err error) error { + if strings.HasSuffix(path, ".jsonl") { + files = append(files, path) + } + return nil + }) + fatalOnError(err) + if len(files) <= 0 { + log.Fatal("no files to process?!") + } + var found int + for _, file := range files { + data, err := ioutil.ReadFile(file) + fatalOnError(err) + measurements := bytes.Split(data, []byte("\n")) + for _, measurement := range measurements { + if len(measurement) <= 0 { + continue + } + var entry Measurement + err = json.Unmarshal(measurement, &entry) + fatalOnError(err) + log.Printf("processing: %+v", entry) + options := []string{ + "run", + "./internal/cmd/apitool", + "-mode", "meta", + "-report-id", entry.ReportID, + } + found++ + if entry.Input != nil { + options = append(options, "-input") + options = append(options, *entry.Input) + } + log.Printf("run: go %s", strings.Join(options, " ")) + cmd := exec.Command("go", options...) + cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr + err = cmd.Run() + fatalOnError(err) + } + } + if found != *expected { + log.Fatalf("expected %d measurements, found %d measurements", *expected, found) + } +}