blob: fc9c3d0edc2302bcfff3051301c7823156a53092 [file] [log] [blame]
package blackbox
import (
"runtime"
"testing"
"veyron/services/store/memstore"
"veyron/services/store/memstore/state"
"veyron/services/store/service"
"veyron2/storage"
"veyron2/vom"
)
func init() {
vom.Register(&Player{})
vom.Register(&Team{})
}
// This schema uses the Team/Player schema in the query doc.
//
// Player : belongs to many teams.
// Team : contains many players.
// Player is a person who has a Role.
type Player struct {
FullName string
}
// Team has a set of Roles/
type Team struct {
FullName string
Players []storage.ID
}
func newPlayer(name string) *Player {
return &Player{FullName: name}
}
func newTeam(name string) *Team {
return &Team{FullName: name}
}
func getPlayer(t *testing.T, st *memstore.Store, tr service.Transaction, path string) (storage.ID, *Player) {
_, file, line, _ := runtime.Caller(1)
e := Get(t, st, tr, path)
p, ok := e.Value.(*Player)
if !ok {
t.Fatalf("%s(%d): %s: not a Player: %v", file, line, path, e.Value)
}
return e.Stat.ID, p
}
func getTeam(t *testing.T, st *memstore.Store, tr service.Transaction, path string) (storage.ID, *Team) {
_, file, line, _ := runtime.Caller(1)
e := Get(t, st, tr, path)
p, ok := e.Value.(*Team)
if !ok {
t.Fatalf("%s(%d): %s: not a Team: %v", file, line, path, e.Value)
}
return e.Stat.ID, p
}
func TestManyToManyWithRole(t *testing.T) {
st, err := memstore.New(rootPublicID, "")
if err != nil {
t.Fatalf("memstore.New() failed: %v", err)
}
john := newPlayer("John")
jane := newPlayer("Jane")
joan := newPlayer("Joan")
rockets := newTeam("Rockets")
hornets := newTeam("Hornets")
// Create the state.
{
tr := memstore.NewTransaction()
Put(t, st, tr, "/", newDir())
Put(t, st, tr, "/teamsapp", newDir())
Put(t, st, tr, "/teamsapp/teams", newDir())
Put(t, st, tr, "/teamsapp/teams/rockets", rockets)
Put(t, st, tr, "/teamsapp/teams/hornets", hornets)
Put(t, st, tr, "/teamsapp/players", newDir())
johnID := Put(t, st, tr, "/teamsapp/teams/rockets/Players/@", john)
janeID := Put(t, st, tr, "/teamsapp/teams/rockets/Players/@", jane)
Put(t, st, tr, "/teamsapp/teams/hornets/Players/@", janeID)
joanID := Put(t, st, tr, "/teamsapp/teams/hornets/Players/@", joan)
Put(t, st, tr, "/teamsapp/players/John", johnID)
Put(t, st, tr, "/teamsapp/players/Jane", janeID)
Put(t, st, tr, "/teamsapp/players/Joan", joanID)
Commit(t, tr)
}
rocketsID := Get(t, st, nil, "/teamsapp/teams/rockets").Stat.ID
hornetsID := Get(t, st, nil, "/teamsapp/teams/hornets").Stat.ID
johnID := Get(t, st, nil, "/teamsapp/players/John").Stat.ID
janeID := Get(t, st, nil, "/teamsapp/players/Jane").Stat.ID
joanID := Get(t, st, nil, "/teamsapp/players/Joan").Stat.ID
// Verify some of the state.
{
tr := memstore.NewTransaction()
_, john := getPlayer(t, st, tr, "/teamsapp/players/John")
_, rockets := getTeam(t, st, tr, "/teamsapp/teams/rockets")
if john.FullName != "John" {
t.Errorf("Expected %q, got %q", "John", john.FullName)
}
if len(rockets.Players) != 2 {
t.Fatalf("Expected two players: got %v", rockets.Players)
}
if rockets.Players[0] != johnID {
t.Errorf("Expected %s, got %s", johnID, rockets.Players[0])
}
}
// Iterate over the rockets.
players := make(map[storage.ID]*Player)
name := storage.ParsePath("/teamsapp/players")
for it := st.Snapshot().NewIterator(rootPublicID, name, nil); it.IsValid(); it.Next() {
e := it.Get()
if p, ok := e.Value.(*Player); ok {
if _, ok := players[e.Stat.ID]; ok {
t.Errorf("Player already exists: %v", p)
continue
}
players[e.Stat.ID] = p
}
}
if len(players) != 3 {
t.Errorf("Should have 3 players: have %v", players)
}
if p, ok := players[johnID]; !ok || p.FullName != "John" {
t.Errorf("Should have John, have %v", p)
}
if p, ok := players[janeID]; !ok || p.FullName != "Jane" {
t.Errorf("Should have Jane, have %v", p)
}
if p, ok := players[joanID]; !ok || p.FullName != "Joan" {
t.Errorf("Should have Joan, have %v", p)
}
// Iterate over all teams, nonrecursively.
teams := make(map[storage.ID]*Team)
name = storage.ParsePath("/teamsapp/teams")
for it := st.Snapshot().NewIterator(rootPublicID, name, state.ImmediateFilter); it.IsValid(); it.Next() {
e := it.Get()
v := e.Value
if _, ok := v.(*Player); ok {
t.Errorf("Nonrecursive iteration should not show players")
}
if team, ok := v.(*Team); ok {
if _, ok := teams[e.Stat.ID]; ok {
t.Errorf("Team already exists: %v", team)
continue
}
teams[e.Stat.ID] = team
}
}
if len(teams) != 2 {
t.Errorf("Should have 2 teams: have %v", teams)
}
if team, ok := teams[rocketsID]; !ok || team.FullName != "Rockets" {
t.Errorf("Should have Rockets, have %v", team)
}
if team, ok := teams[hornetsID]; !ok || team.FullName != "Hornets" {
t.Errorf("Should have Hornets, have %v", team)
}
// Iterate over all teams, recursively.
playerCount := 0
teamCount := 0
players = make(map[storage.ID]*Player)
teams = make(map[storage.ID]*Team)
name = storage.ParsePath("/teamsapp/teams")
for it := st.Snapshot().NewIterator(rootPublicID, name, nil); it.IsValid(); it.Next() {
e := it.Get()
v := e.Value
if p, ok := v.(*Player); ok {
players[e.Stat.ID] = p
playerCount++
}
if team, ok := v.(*Team); ok {
teams[e.Stat.ID] = team
teamCount++
}
}
if teamCount != 2 {
t.Errorf("Should have 2 teams: have %d", teamCount)
}
if len(teams) != 2 {
t.Errorf("Should have 2 teams: have %v", teams)
}
if team, ok := teams[rocketsID]; !ok || team.FullName != "Rockets" {
t.Errorf("Should have Rockets, have %v", team)
}
if team, ok := teams[hornetsID]; !ok || team.FullName != "Hornets" {
t.Errorf("Should have Hornets, have %v", team)
}
if playerCount != 3 {
t.Errorf("Should have 3 players: have %d", playerCount)
}
if len(players) != 3 {
t.Errorf("Should have 3 players: have %v", players)
}
if p, ok := players[johnID]; !ok || p.FullName != "John" {
t.Errorf("Should have John, have %v", p)
}
if p, ok := players[janeID]; !ok || p.FullName != "Jane" {
t.Errorf("Should have Jane, have %v", p)
}
if p, ok := players[joanID]; !ok || p.FullName != "Joan" {
t.Errorf("Should have Joan, have %v", p)
}
}