brother: emits declared in the module's own list too, parts of 700 KiB at most, a duplicates window on device-asks, and the built binary ignored (issue 1453)
This commit is contained in:
@@ -24,3 +24,4 @@ modules/mesh-files/cmd/mesh-files/mesh-files
|
||||
|
||||
# The provisioner client built in place (issue 671).
|
||||
modules/mesh-provisioner-client/cmd/mesh-provisioner-client/mesh-provisioner-client
|
||||
modules/brother/cmd/brother/brother
|
||||
|
||||
@@ -79,6 +79,14 @@ it for the device process alone; the tools' bundle is given no such name, and `a
|
||||
no tool file imports the package that reads it. While it is not given, `printer.settings` answers that the secret
|
||||
is not given, and the printer is not contacted.
|
||||
|
||||
### Documents in parts
|
||||
|
||||
The bus refuses a message over 1 MiB, so a document crosses it in parts of at most 700 KiB (both the size this module
|
||||
cuts and the most it accepts; ADR 0468 clause 3 says 768 KiB, which is 1 MiB of base64 before the message around it),
|
||||
counted from 1, each carried base64-encoded in JSON. A caller joins the parts and checks the SHA-256. The bucket
|
||||
(`s3-bucket`, from `minio`) is spoken to by the device process alone, from `store.json` (owner `brother-device`, mode
|
||||
0600), with the S3 client mesh-mail uses; a scan is kept once as `<sha256>.pdf` and an upload as `uploads/<id>.<ext>`.
|
||||
|
||||
### The device process
|
||||
|
||||
`brother device` runs as `brother-device` with a bus account of its own (the own secret `broker-device`), never a
|
||||
|
||||
@@ -24,7 +24,7 @@ func pdfOf(size int) []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
// 1453/AC10: a document is cut into parts of at most PartSize, under the 768 KiB limit, and the parts joined in
|
||||
// 1453/AC10: a document is cut into parts of at most PartSize, under the limit, and the parts joined in
|
||||
// order are the document, with the SHA-256 checked; a wrong SHA-256 or a part missing is refused; a part out of
|
||||
// range is refused naming the range.
|
||||
func TestADocumentIsCutIntoPartsAndJoinedWithItsSha256Checked(t *testing.T) {
|
||||
@@ -438,3 +438,24 @@ func TestAScanAndAPrintAreAcknowledgedBeforeTheWork(t *testing.T) {
|
||||
t.Errorf("settings: early called %d times, action %v", calls, act)
|
||||
}
|
||||
}
|
||||
|
||||
// 1453/AC11: a part of 700 KiB is taken and one of a byte more is refused, by the module's own message naming
|
||||
// 700 KiB, before it is queued; the limit and the size the module cuts are one.
|
||||
func TestAPartOverSevenHundredKiBIsRefusedByTheModulesOwnMessage(t *testing.T) {
|
||||
if MaxPart != PartSize || PartSize != 700<<10 {
|
||||
t.Fatalf("MaxPart %d, PartSize %d: both are 700 KiB", MaxPart, PartSize)
|
||||
}
|
||||
r := newRig(t, `{}`, "pw")
|
||||
big := make([]byte, 700<<10+1)
|
||||
_, err := upload(t, r, "over", big, sha256Hex(big), 1, 1, big)
|
||||
if err == nil || !strings.Contains(err.Error(), "700 KiB") {
|
||||
t.Fatalf("a part of 700 KiB + 1 byte: error = %v, want the module's own message naming 700 KiB", err)
|
||||
}
|
||||
if r.bus.asks() != 0 {
|
||||
t.Errorf("an oversized part was queued")
|
||||
}
|
||||
ok := pdfOf(700 << 10)
|
||||
if _, err := upload(t, r, "exact", ok, sha256Hex(ok), 1, 1, ok); err != nil {
|
||||
t.Errorf("a part of exactly 700 KiB: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ func tools(settingsFile string, now func() time.Time) []stdio.Tool {
|
||||
{
|
||||
Name: "printer.upload",
|
||||
Description: "Send one part of a document to the device process, to be printed later (printer.print with the upload id). " +
|
||||
"Cut the document into parts of at most 700 KiB (768 KiB is the limit), number them from 1, and send each with the " +
|
||||
"Cut the document into parts of at most 700 KiB (700 KiB is the limit), number them from 1, and send each with the " +
|
||||
"whole document's SHA-256. When the last part has come and the joined SHA-256 matches, the document is stored as " +
|
||||
"uploads/<upload>.pdf or .jpg and the answer says complete; a wrong SHA-256 stores nothing. PDF and JPEG only.",
|
||||
Input: map[string]any{"type": "object", "required": []string{"upload", "part", "parts", "sha256", "data"}, "properties": map[string]any{
|
||||
|
||||
@@ -64,8 +64,11 @@ func TestTheScanFolderIsNoManifestResource(t *testing.T) {
|
||||
// 1440/AC9: the event the code emits is the one the manifest declares, and the tools it serves are named.
|
||||
func TestTheManifestDeclaresWhatTheCodeServes(t *testing.T) {
|
||||
m := readManifest(t)
|
||||
if len(m.Emits) != 0 {
|
||||
t.Errorf("the module's own emits = %v: scan.added is said by the device process, whose bus block declares it", m.Emits)
|
||||
// Declared in both places, as mesh-mail declares its sender's events: the module's own, and the device
|
||||
// process's bus block (the controller refuses a process's emit the module does not declare as its own,
|
||||
// ADR 0436 §1).
|
||||
if !reflect.DeepEqual(m.Emits, []string{EventAdded}) {
|
||||
t.Errorf("the module's own emits = %v, want [%s]", m.Emits, EventAdded)
|
||||
}
|
||||
bus, _ := resource(t, m, "device")["bus"].(map[string]any)
|
||||
if !reflect.DeepEqual(bus["emits"], []any{EventAdded}) {
|
||||
@@ -312,6 +315,9 @@ func TestThePrinterAsksStreamIsAWorkQueue(t *testing.T) {
|
||||
!reflect.DeepEqual(st["events"], []any{EventAsked}) {
|
||||
t.Errorf("stream = %v", st)
|
||||
}
|
||||
if st["duplicates"] != float64(24) {
|
||||
t.Errorf("the stream has no duplicates window like mail-sends': %v", st["duplicates"])
|
||||
}
|
||||
if st["max-age-days"] == nil || st["max-mib"] == nil {
|
||||
t.Errorf("the stream is bounded: %v", st)
|
||||
}
|
||||
|
||||
@@ -9,11 +9,12 @@ import (
|
||||
// Documents cross the bus in parts (novox/hq ADR 0468 clause 3): the bus refuses a message over 1 MiB, and a
|
||||
// part travels base64-encoded inside a JSON message.
|
||||
const (
|
||||
// MaxPart is the most raw bytes a part may hold; a part of more is refused.
|
||||
MaxPart = 768 << 10
|
||||
// PartSize is how large the parts this module cuts are: below MaxPart, so that the part's base64 text and
|
||||
// the message around it stay under the bus's 1 MiB.
|
||||
// PartSize is how large the parts this module cuts are, and MaxPart the most raw bytes a part may hold; a part
|
||||
// of more is refused. They are one: 700 KiB, so that a part's base64 text (about 933 KB) and the message around
|
||||
// it stay under the bus's 1 MiB. (ADR 0468 clause 3 says at most 768 KiB, which is 1 MiB of base64 before the
|
||||
// message around it, and so over the bus's limit.)
|
||||
PartSize = 700 << 10
|
||||
MaxPart = PartSize
|
||||
// MostParts bounds how many parts one document has (a 100 MiB document is the most).
|
||||
MostParts = 150
|
||||
)
|
||||
|
||||
@@ -209,7 +209,7 @@ func checkUpload(a Ask) error {
|
||||
return fmt.Errorf("data is not base64: %v", err)
|
||||
}
|
||||
if len(raw) > MaxPart {
|
||||
return fmt.Errorf("a part is at most %d bytes (768 KiB), not %d", MaxPart, len(raw))
|
||||
return fmt.Errorf("a part is at most %d bytes (700 KiB), not %d", MaxPart, len(raw))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -89,7 +89,8 @@
|
||||
"max-age-days": 1,
|
||||
"max-mib": 8,
|
||||
"retention": "work-queue",
|
||||
"max-deliveries": 3
|
||||
"max-deliveries": 3,
|
||||
"duplicates": 24
|
||||
}
|
||||
],
|
||||
"state": [
|
||||
@@ -142,6 +143,9 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"emits": [
|
||||
"scan.added"
|
||||
],
|
||||
"tools": [
|
||||
"scanner.scan",
|
||||
"scanner.status",
|
||||
@@ -183,7 +187,8 @@
|
||||
"crop"
|
||||
],
|
||||
"merge": "json",
|
||||
"template": "files/settings.json"
|
||||
"template": "files/settings.json",
|
||||
"trusted": true
|
||||
},
|
||||
{
|
||||
"id": "store",
|
||||
|
||||
Reference in New Issue
Block a user