Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions b2/accountinfo_edges_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package b2

import (
"bytes"
"encoding/json"
"errors"
"os"
"path/filepath"
"strings"
"testing"
)

func TestAccountInfoWriteSizeBoundary(t *testing.T) {
for _, test := range []struct {
name string
size int
want bool
}{
{"cap minus one", 1_048_575, true},
{"cap", 1_048_576, true},
{"cap plus one", 1_048_577, false},
} {
t.Run(test.name, func(t *testing.T) {
data := sampleAuthData()
data.AuthToken = ""
empty, err := json.Marshal(data)
if err != nil {
t.Fatal(err)
}
data.AuthToken = strings.Repeat("x", test.size-len(empty)-1)
path := filepath.Join(t.TempDir(), "private", "session.json")
store := NewAccountInfoFile(path)
store.Set(data)
if !test.want {
if err := store.Err(); err == nil || !strings.Contains(err.Error(), "exceeds") {
t.Fatalf("Set %d-byte session error = %v, want size rejection", test.size, err)
}
if _, err := os.Stat(path); !os.IsNotExist(err) {
t.Fatalf("rejected %d-byte session created a file: %v", test.size, err)
}
return
}
if err := store.Err(); err != nil {
t.Fatalf("Set %d-byte session: %v", test.size, err)
}
content, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if len(content) != test.size {
t.Fatalf("session file bytes = %d, want %d", len(content), test.size)
}
})
}
}

func TestAccountInfoReadSizeBoundary(t *testing.T) {
for _, test := range []struct {
name string
size int
want bool
}{
{"cap minus one", 1_048_575, true},
{"cap", 1_048_576, true},
{"cap plus one", 1_048_577, false},
} {
t.Run(test.name, func(t *testing.T) {
parent := filepath.Join(t.TempDir(), "private")
if err := os.Mkdir(parent, 0o700); err != nil {
t.Fatal(err)
}
path := filepath.Join(parent, "session.json")
content := append([]byte("{}"), bytes.Repeat([]byte(" "), test.size-2)...)
if err := os.WriteFile(path, content, 0o600); err != nil {
t.Fatal(err)
}
store := NewAccountInfoFile(path)
_, ok := store.AuthData()
if test.want {
if !ok || store.Err() != nil {
t.Fatalf("AuthData() for %d-byte session: ok=%t err=%v, want loaded", test.size, ok, store.Err())
}
} else if ok || !errors.Is(store.Err(), errUnavailableAccountInfoSession) {
t.Fatalf("AuthData() for %d-byte session: ok=%t err=%v, want size rejection", test.size, ok, store.Err())
}
})
}
}
4 changes: 2 additions & 2 deletions b2/accountinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func TestAccountInfoFileRoundTripAndPermissions(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !json.Valid(content) || strings.Contains(string(content), "application-key-secret") {
t.Fatal("file is not valid JSON or contains the application key")
if !json.Valid(content) {
t.Fatal("file is not valid JSON")
}
fileInfo, err := os.Stat(path)
if err != nil {
Expand Down
100 changes: 100 additions & 0 deletions b2/core_edges_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package b2

import (
"context"
"strings"
"testing"
"time"
)

func TestObjectLockUpdateInvalidatesCachedInfo(t *testing.T) {
for _, test := range []struct {
name string
update func(*Object) error
}{
{"retention", func(o *Object) error {
return o.UpdateFileRetention(context.Background(), &FileRetention{Mode: RetentionGovernance, RetainUntil: time.UnixMilli(1_735_689_600_000)}, false)
}},
{"legal hold", func(o *Object) error {
return o.UpdateFileLegalHold(context.Background(), LegalHoldOn)
}},
} {
t.Run(test.name, func(t *testing.T) {
wire := newLockWire(t)
object := wire.object(t)
ctx := context.Background()
wire.info = `{"fileId":"id","fileName":"name","action":"upload","contentLength":1,"contentType":"text/plain","fileInfo":{"marker":"before"}}`
before, err := object.Attrs(ctx)
if err != nil {
t.Fatal(err)
}
if before.Info["marker"] != "before" {
t.Fatalf("initial file info marker = %q, want before", before.Info["marker"])
}
calls := wire.calls
if err := test.update(object); err != nil {
t.Fatal(err)
}
wire.info = `{"fileId":"id","fileName":"name","action":"upload","contentLength":1,"contentType":"text/plain","fileInfo":{"marker":"after"}}`
after, err := object.Attrs(ctx)
if err != nil {
t.Fatal(err)
}
if after.Info["marker"] != "after" || wire.calls != calls+2 || wire.path != "/b2api/v4/b2_get_file_info" {
t.Fatalf("post-update marker=%q calls=%d (want %d) last path=%q", after.Info["marker"], wire.calls, calls+2, wire.path)
}
})
}
}

func TestObjectDeleteRejectsFolderStatus(t *testing.T) {
file := &testFile{n: "folder", a: "folder", files: map[string]string{"folder": "present"}}
object := objectForDeleteOptionTest(file)
if err := object.Delete(context.Background()); err == nil || !strings.Contains(err.Error(), "not a regular file or hide marker") {
t.Fatalf("Delete(folder) error = %v, want non-regular-file rejection", err)
}
if got := file.files["folder"]; got != "present" {
t.Fatalf("Delete(folder) changed file state to %q", got)
}
}

func TestMemoryBufferCloseIsIdempotent(t *testing.T) {
buffer := newMemoryBuffer()
if _, err := buffer.Write([]byte("payload")); err != nil {
t.Fatal(err)
}
if err := buffer.Close(); err != nil {
t.Fatal(err)
}
if buffer.buf != nil {
t.Fatal("Close retained the buffer")
}
if err := buffer.Close(); err != nil {
t.Fatalf("second Close: %v", err)
}
}

func TestProgressZeroWakeDoesNotDuplicateCallback(t *testing.T) {
var samples []int64
p := &progressReporter{
callback: func(completed, _ int64) { samples = append(samples, completed) },
total: 1,
wake: make(chan struct{}), // A send acknowledges that run received the wake.
stop: make(chan struct{}),
done: make(chan struct{}),
}
go p.run()
p.add(1)
deadline := time.After(5 * time.Second)
for i := 0; i < 2; i++ {
select {
case p.wake <- struct{}{}:
case <-deadline:
t.Fatal("progress reporter did not receive a wake")
}
}
p.finish()
if len(samples) != 2 || samples[0] != 1 || samples[1] != 1 {
t.Fatalf("progress callbacks = %v, want one nonzero update and one terminal call", samples)
}
}
Loading