158 lines
5.4 KiB
Go
158 lines
5.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// 1601/AC1: scanner.scan reads the stored PDF by the GET link the device's answer carries (never by parts) and writes it
|
|
// into the scan folder; the answer is the event's body plus the path.
|
|
func TestTheScanIsReadFromTheStoreByTheLinkAndFiled(t *testing.T) {
|
|
f := newFakeScanner(t, fixture(t, "scan.jpg"))
|
|
r, folder := scanRig(t, f, Settings{Crop: false})
|
|
body, err := r.scan(t)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(*r.said) != 1 {
|
|
t.Fatalf("%d events", len(*r.said))
|
|
}
|
|
stored, err := r.objects.Get(t.Context(), (*r.said)[0].body["object"].(string))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
filed, err := os.ReadFile(body["path"].(string))
|
|
if err != nil || string(filed) != string(stored) {
|
|
t.Fatalf("the filed scan is not the stored one: %v", err)
|
|
}
|
|
if filepath.Dir(body["path"].(string)) != folder {
|
|
t.Errorf("filed in %s, want %s", filepath.Dir(body["path"].(string)), folder)
|
|
}
|
|
for _, a := range r.bus.published {
|
|
if a.Op == OpDocument {
|
|
t.Errorf("a part of the scan was asked of the device process over the bus")
|
|
}
|
|
}
|
|
for k := range (*r.said)[0].body {
|
|
if body[k] == nil && k != "path" {
|
|
t.Errorf("the event's %s is not in the answer", k)
|
|
}
|
|
}
|
|
if body["link"] != nil || body["parts"] != nil || body["key"] != nil {
|
|
t.Errorf("the answer carries the transport's own fields: %v", body)
|
|
}
|
|
}
|
|
|
|
// 1601/AC2: the device's answer carries a link that expires within ten minutes and no bytes of the document; the real
|
|
// store signs it with the binding's key for the host the binding gives.
|
|
func TestTheDeviceAnswerCarriesALinkAndNoBytes(t *testing.T) {
|
|
f := newFakeScanner(t, fixture(t, "scan.jpg"))
|
|
r, _ := scanRig(t, f, Settings{Crop: false})
|
|
if _, err := r.scan(t); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var ans map[string]any
|
|
for _, raw := range r.answers.m {
|
|
json.Unmarshal(raw, &ans)
|
|
}
|
|
link, _ := ans["link"].(string)
|
|
u, err := url.Parse(link)
|
|
if err != nil || link == "" {
|
|
t.Fatalf("answer %v", ans)
|
|
}
|
|
secs, _ := strconv.Atoi(u.Query().Get("X-Amz-Expires"))
|
|
if secs < 1 || time.Duration(secs)*time.Second > 10*time.Minute {
|
|
t.Errorf("the link lives %d s", secs)
|
|
}
|
|
raw, _ := json.Marshal(ans)
|
|
if ans["data"] != nil || len(raw) > 4096 {
|
|
t.Errorf("the answer is %d bytes and carries data %v", len(raw), ans["data"] != nil)
|
|
}
|
|
|
|
// The real store: the binding's key signs, for the binding's host and port, in the module's bucket.
|
|
s3 := NewS3(StoreFacts{Scheme: "http", At: "novox.internal", Port: "20001", Bucket: "mesh-shanks-brother", Region: "eu-west",
|
|
Key: "binding-key", Secret: "binding-secret"})
|
|
s3.Now = func() time.Time { return fixedNow }
|
|
got, err := s3.Link("GET", "abc.pdf", 10*time.Minute)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
gu, _ := url.Parse(got)
|
|
if gu.Host != "novox.internal:20001" || gu.Path != "/mesh-shanks-brother/abc.pdf" ||
|
|
!strings.HasPrefix(gu.Query().Get("X-Amz-Credential"), "binding-key/") || gu.Query().Get("X-Amz-Expires") != "600" {
|
|
t.Errorf("link %s", got)
|
|
}
|
|
if _, err := s3.Link("GET", "abc.pdf", 11*time.Minute); err == nil {
|
|
t.Error("a link over ten minutes was signed")
|
|
}
|
|
}
|
|
|
|
// 1601/AC3: a body whose SHA-256 differs from the scan's writes no file and answers the mismatch.
|
|
func TestABodyOfAnotherHashWritesNoFile(t *testing.T) {
|
|
f := newFakeScanner(t, fixture(t, "scan.jpg"))
|
|
r, folder := scanRig(t, f, Settings{Crop: false})
|
|
r.objects.corrupt = true
|
|
_, err := r.scan(t)
|
|
if err == nil || !strings.Contains(err.Error(), "hashes to") {
|
|
t.Fatalf("error = %v", err)
|
|
}
|
|
if e, _ := os.ReadDir(folder); len(e) != 0 {
|
|
t.Errorf("a scan of the wrong hash was filed: %v", e)
|
|
}
|
|
}
|
|
|
|
// 1601/AC4: brother.scan.added is said before the file is written, once the scan is stored, and is under 4 KiB.
|
|
func TestTheEventIsSaidBeforeTheFileIsWritten(t *testing.T) {
|
|
f := newFakeScanner(t, fixture(t, "scan.jpg"))
|
|
r, folder := scanRig(t, f, Settings{Crop: false})
|
|
var filedWhenSaid = -1
|
|
emit := r.worker.Emit
|
|
r.worker.Emit = func(body map[string]any, id string) error {
|
|
e, _ := os.ReadDir(folder)
|
|
filedWhenSaid = len(e)
|
|
return emit(body, id)
|
|
}
|
|
if _, err := r.scan(t); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if filedWhenSaid != 0 {
|
|
t.Errorf("%d files in the folder when the event was said, want 0", filedWhenSaid)
|
|
}
|
|
said := (*r.said)[0]
|
|
if !said.seen {
|
|
t.Error("the event was said before the scan was stored")
|
|
}
|
|
raw, _ := json.Marshal(said.body)
|
|
if len(raw) >= 4096 || said.body["path"] != nil {
|
|
t.Errorf("the event is %d bytes, path %v", len(raw), said.body["path"])
|
|
}
|
|
}
|
|
|
|
// 1601/AC5: no code of the device process writes a file outside its state directory: the only writes to the disk in the
|
|
// module's code are the filing of a scan (file.go), made by the tool side (scan.go).
|
|
func TestOnlyTheFilingWritesFiles(t *testing.T) {
|
|
writes := []string{"os.WriteFile", "os.Create(", "os.OpenFile", "os.MkdirAll", "os.Mkdir(", "os.Rename", "os.CreateTemp", "File(s.Settings.Folder"}
|
|
files, _ := filepath.Glob("*.go")
|
|
for _, name := range files {
|
|
if strings.HasSuffix(name, "_test.go") || name == "file.go" {
|
|
continue
|
|
}
|
|
raw, _ := os.ReadFile(name)
|
|
for _, w := range writes {
|
|
if strings.Contains(string(raw), w) && !(name == "scan.go" && w == "File(s.Settings.Folder") {
|
|
t.Errorf("%s writes with %s: only file.go does, and scan.go (the tool side) calls it", name, w)
|
|
}
|
|
}
|
|
}
|
|
raw, _ := os.ReadFile("deviceproc.go")
|
|
if strings.Contains(string(raw), "File(") {
|
|
t.Error("deviceproc.go files a scan")
|
|
}
|
|
}
|