func TestUpdateEntryWithEtagBadKey(t *testing.T) { origEntry := *kOrigEntry var store FakeStore newId, err := vsafedb.AddEntry(&store, nil, kKey, &origEntry) if err != nil { t.Fatalf("Error saving original entry %v", err) } var origEntryWithEtag vsafe.EntryWithEtag if err := vsafedb.EntryByIdWithEtag( store, nil, newId, kKey, &origEntryWithEtag); err != nil { t.Fatalf("Error readingoriginal entry %v", err) } update := functional.NewFilterer(changeToAnEntry) badKey := *kKey badKey.Id++ if err := vsafedb.UpdateEntryWithEtag( store, kTransaction, newId, origEntryWithEtag.Etag, &badKey, update); err != vsafedb.ErrNoSuchId { t.Errorf("Expected ErrNoSuchId, got %v", err) } var readEntry vsafe.Entry if err := vsafedb.EntryById(store, nil, newId, kKey, &readEntry); err != nil { t.Fatalf("Error reading store: %v", err) } if readEntry != origEntryWithEtag.Entry { t.Errorf("Entry should not have been updated") } }
func TestUpdateEntryWithEtag(t *testing.T) { origEntry := *kOrigEntry var store FakeStore newId, err := vsafedb.AddEntry(&store, nil, kKey, &origEntry) if err != nil { t.Fatalf("Error saving original entry %v", err) } var origEntryWithEtag vsafe.EntryWithEtag if err := vsafedb.EntryByIdWithEtag( store, nil, newId, kKey, &origEntryWithEtag); err != nil { t.Fatalf("Error reading original entry %v", err) } update := functional.NewFilterer(changeToAnEntry) if err := vsafedb.UpdateEntryWithEtag( store, kTransaction, newId, origEntryWithEtag.Etag, kKey, update); err != nil { t.Fatalf("Error updating store: %v", err) } var readEntry vsafe.Entry if err := vsafedb.EntryById(store, nil, newId, kKey, &readEntry); err != nil { t.Fatalf("Error reading store: %v", err) } entry := *kAnEntry entry.Owner = readEntry.Owner entry.Id = readEntry.Id if readEntry != entry { t.Errorf("Expected %v, got %v", entry, readEntry) } }
func TestUpdateEntryConcurrent(t *testing.T) { origEntry := *kOrigEntry var store FakeStore newId, err := vsafedb.AddEntry(&store, nil, kKey, &origEntry) if err != nil { t.Fatalf("Error saving original entry %v", err) } var origEntryWithEtag vsafe.EntryWithEtag if err := vsafedb.EntryByIdWithEtag( store, nil, newId, kKey, &origEntryWithEtag); err != nil { t.Fatalf("Error reading original entry %v", err) } update := functional.NewFilterer(changeToAnEntry) updateSkipped := functional.NewFilterer(func(ptr interface{}) error { entryPtr := ptr.(*vsafe.Entry) *entryPtr = *kAnEntry return functional.Skipped }) // An update that skips shouldn't throw an error even if etag is wrong if err := vsafedb.UpdateEntryWithEtag( store, kTransaction, newId, origEntryWithEtag.Etag+1, kKey, updateSkipped); err != nil { t.Fatalf("Error updating store: %v", err) } if err := vsafedb.UpdateEntryWithEtag( store, kTransaction, newId, origEntryWithEtag.Etag+1, kKey, update); err != vsafedb.ErrConcurrentModification { t.Errorf("Expected ErrConcurrentModfication, got %v", err) } var readEntry vsafe.Entry if err := vsafedb.EntryById(store, nil, newId, kKey, &readEntry); err != nil { t.Fatalf("Error reading store: %v", err) } if readEntry != origEntryWithEtag.Entry { t.Errorf("Entry should not have been updated") } }
func (h *Handler) doGet(w http.ResponseWriter, r *http.Request, id int64) { session := common.GetUserSession(r) if isIdValid(id) { var entryWithEtag vsafe.EntryWithEtag err := vsafedb.EntryByIdWithEtag( h.Store, nil, id, session.Key(), &entryWithEtag) if err == vsafedb.ErrNoSuchId { fmt.Fprintln(w, "No entry found.") return } if err != nil { http_util.ReportError(w, "Error reading database.", err) return } http_util.WriteTemplate( w, kTemplate, newView( fromEntry(&entryWithEtag.Entry, entryWithEtag.Etag), true, session.Key().Id, common.NewXsrfToken(r, kSingle), nil)) } else { initValues := make(url.Values) initValues.Set("url", "http://") // Because this page is always POST, the presence of etag signals that // we are editing an entry, not fetching for the first time. // The value of etag in this context does not matter since we are editing // a new entry. initValues.Set("etag", "new") http_util.WriteTemplate( w, kTemplate, newView( initValues, false, session.Key().Id, common.NewXsrfToken(r, kSingle), nil)) } }