func TestDateRangeBuilder(t *testing.T) { const expected = `select * from event where key in ($1) and created >= $2 and created < $3 order by created desc;` q := straumur.Query{} q.Key = "somekey" q.From = time.Date(2013, time.September, 20, 9, 1, 45, 0, time.UTC) q.To = time.Date(2013, time.September, 20, 9, 1, 47, 0, time.UTC) expectedArgs := []interface{}{q.Key, q.From, q.To} s, args := buildSelectQuery(q) if s != expected { t.Fatalf("Expected %s, got %s", expected, s) } if len(args) != len(expectedArgs) { t.Fatalf("Expected %+v, got %+v", expectedArgs, args) } }
func TestQueryBuilder(t *testing.T) { const expected = `select * from event where key in ($1, $2) and origin = $3 and entities @> ARRAY[$4, $5]::text[] order by created desc;` expectedArgs := []interface{}{"foo.bar", "bar.foo", "mysystem", "c/1", "c/2"} q := straumur.Query{} q.Origin = "mysystem" q.Entities = []string{"c/1", "c/2"} q.Key = "foo.bar OR bar.foo" s, args := buildSelectQuery(q) if s != expected { t.Fatalf("Expected %s, got %s", expected, s) } if len(args) != len(expectedArgs) { t.Fatalf("Expected %+v, got %+v", expectedArgs, args) } }
func TestAggregateBuilder(t *testing.T) { const expected = `select i as name, count(*) as count from (select unnest(actors) as i from (select * from event where key in ($1, $2) and origin = $3 and entities @> ARRAY[$4, $5]::text[] order by created desc) x order by actors) t group by i order by i;` expectedArgs := []interface{}{"foo.bar", "bar.foo", "mysystem", "c/1", "c/2"} q := straumur.Query{} q.Origin = "mysystem" q.Entities = []string{"c/1", "c/2"} q.Key = "foo.bar OR bar.foo" s, args := buildAggregateQuery(q, "actors") if s != expected { t.Fatalf("Expected %s, got %s", expected, s) } if len(args) != len(expectedArgs) { t.Fatalf("Expected %+v, got %+v", expectedArgs, args) } }
func (p *PostgresDataSource) AggregateType(q straumur.Query, s string) (map[string]int, error) { if !q.IsValidArrayType(s) { return nil, errors.New("Invalid type") } query, args := buildAggregateQuery(q, s) m := make(map[string]int) err := p.wrapTransaction(func(tx *sql.Tx) error { rows, err := tx.Query(query, args...) defer rows.Close() for rows.Next() { var cStr string var cInt int rows.Scan(&cStr, &cInt) m[cStr] = cInt } return err }) return m, err }
func TestWebSocketBroadcaster(t *testing.T) { url := fmt.Sprintf("ws://%s%s", serverAddr, "/ws") conn, err := websocket.Dial(url, "", "http://localhost/") if err != nil { t.Errorf("WebSocket handshake error: %v", err) return } q := straumur.Query{} q.Entities = []string{"ns/moo"} t.Logf("Query filter: %+v", q) websocket.JSON.Send(conn, q) time.Sleep(300 * time.Millisecond) e := straumur.NewEvent( "myapp.user.login", nil, nil, "User foobar logged in", 3, "myapp", []string{"ns/foo", "ns/moo"}, nil, nil, nil) broadcaster.Broadcast(e) var event straumur.Event if err := websocket.JSON.Receive(conn, &event); err != nil { t.Errorf("Read: %v", err) } incoming := make(chan straumur.Event) go readEvents(conn, incoming) filtered := straumur.NewEvent( "Should filter", nil, nil, "This event should be filtered", 3, "myapp", []string{"ns/foo", "ns/boo"}, nil, nil, nil) if q.Match(*filtered) == true { t.Errorf("Query %+v should not pass %+v", q, filtered) } broadcaster.Broadcast(filtered) broadcaster.Broadcast(straumur.NewEvent( "foo.bar", nil, nil, "This event should pass", 3, "myapp", []string{"ns/foo", "ns/moo"}, nil, nil, nil)) ev := <-incoming if ev.Key != "foo.bar" { t.Errorf("Unexpected %s", ev) } }
func buildSelectQuery(q straumur.Query) (string, []interface{}) { var buffer bytes.Buffer args := []interface{}{} paramCount := 1 delimiter := " and " writeDelimiter := false buffer.WriteString("select * from event") //Early exit, query is empty if q.IsEmpty() { buffer.WriteString(" order by created desc;") return buffer.String(), args } buffer.WriteString(" where ") if q.Key != "" { buffer.WriteString("key in (") keys := strings.Split(q.Key, "OR") for arrIdx, s := range keys { args = append(args, strings.TrimSpace(s)) buffer.WriteString(fmt.Sprintf("$%d", paramCount)) paramCount++ if arrIdx+1 < len(keys) { buffer.WriteString(", ") } } buffer.WriteString(")") writeDelimiter = true } if q.Origin != "" { if writeDelimiter { buffer.WriteString(delimiter) } buffer.WriteString(fmt.Sprintf("origin = $%d", paramCount)) args = append(args, q.Origin) paramCount++ writeDelimiter = true } if !q.From.IsZero() { if writeDelimiter { buffer.WriteString(delimiter) } buffer.WriteString(fmt.Sprintf("created >= $%d", paramCount)) args = append(args, q.From) paramCount++ writeDelimiter = true } if !q.To.IsZero() { if writeDelimiter { buffer.WriteString(delimiter) } buffer.WriteString(fmt.Sprintf("created < $%d", paramCount)) args = append(args, q.To) paramCount++ writeDelimiter = true } //Array fields array_fields := make(map[string][]string) array_fields["entities"] = q.Entities array_fields["actors"] = q.Actors for db_name, array_field := range array_fields { if len(array_field) > 0 { if writeDelimiter { buffer.WriteString(delimiter) } nextParam, s := writeArray(paramCount, &args, db_name, array_field) paramCount = nextParam buffer.WriteString(s) writeDelimiter = true } } //todo add sort to query buffer.WriteString(" order by created desc;") return buffer.String(), args }