1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package libkb
5
6import (
7	"strings"
8	"testing"
9
10	"github.com/stretchr/testify/require"
11)
12
13func testLexer(t *testing.T, name string, s string, expected []Token) {
14	lexer := NewLexer(s)
15	i := 0
16	for {
17		tok := lexer.Get()
18		if i >= len(expected) {
19			t.Errorf("%s, unexpected token %d [T%v: '%v']", name, i, tok.Typ, string(tok.value))
20			break
21		}
22		if !tok.Eq(expected[i]) {
23			t.Errorf("%s, token %d: [T%v: '%v'] != [T%v: '%v']",
24				name, i, tok.Typ, string(tok.value), expected[i].Typ, string(expected[i].value))
25		}
26		if tok.Typ == EOF {
27			break
28		}
29		i++
30	}
31}
32
33func TestLexer1(t *testing.T) {
34	s := "http://foo.com && http://bar.com"
35	expected := []Token{
36		{URL, []byte("http://foo.com")},
37		{AND, []byte("&&")},
38		{URL, []byte("http://bar.com")},
39		{EOF, []byte{}},
40	}
41	testLexer(t, "test1", s, expected)
42}
43
44func TestLexer2(t *testing.T) {
45	s := "   (   a    && b          ) || (  c && d && e )   "
46	expected := []Token{
47		{LPAREN, []byte("(")},
48		{URL, []byte("a")},
49		{AND, []byte("&&")},
50		{URL, []byte("b")},
51		{RPAREN, []byte(")")},
52		{OR, []byte("||")},
53		{LPAREN, []byte("(")},
54		{URL, []byte("c")},
55		{AND, []byte("&&")},
56		{URL, []byte("d")},
57		{AND, []byte("&&")},
58		{URL, []byte("e")},
59		{RPAREN, []byte(")")},
60		{EOF, []byte{}},
61	}
62	testLexer(t, "test2", s, expected)
63}
64
65func TestLexer3(t *testing.T) {
66	s := "aa && |bb"
67	expected := []Token{
68		{URL, []byte("aa")},
69		{AND, []byte("&&")},
70		{ERROR, []byte("|bb")},
71		{EOF, []byte{}},
72	}
73	testLexer(t, "test3", s, expected)
74}
75
76func TestLexerSquareBrackets(t *testing.T) {
77	s := "michal,[michal@zapu.net]@email"
78	expected := []Token{
79		{URL, []byte("michal")},
80		{OR, []byte(",")},
81		{URL, []byte("[michal@zapu.net]@email")},
82		{EOF, []byte{}},
83	}
84	testLexer(t, "square brackets", s, expected)
85}
86
87func TestLexerEmailInvalidParentheses(t *testing.T) {
88	// Rejected proposal for round brackets, but make sure
89	// there is expected way in which it is parsed.
90
91	s := "michal,(michal@zapu.net)@email"
92	expected := []Token{
93		{URL, []byte("michal")},
94		{OR, []byte(",")},
95		{LPAREN, []byte("(")},
96		{URL, []byte("michal@zapu.net")},
97		{RPAREN, []byte(")")},
98		{URL, []byte("@email")},
99		{EOF, []byte{}},
100	}
101	testLexer(t, "round brackets", s, expected)
102
103	s = "twitter://alice&&(alice@keybasers.de)@email"
104	expected = []Token{
105		{URL, []byte("twitter://alice")},
106		{AND, []byte("&&")},
107		{LPAREN, []byte("(")},
108		{URL, []byte("alice@keybasers.de")},
109		{RPAREN, []byte(")")},
110		{URL, []byte("@email")},
111		{EOF, []byte{}},
112	}
113	testLexer(t, "round brackets", s, expected)
114}
115
116func TestLexerEmailPlusSign(t *testing.T) {
117	s := "twitter://alice&&[a.li.c+e@keybasers.de]@email"
118	expected := []Token{
119		{URL, []byte("twitter://alice")},
120		{AND, []byte("&&")},
121		{URL, []byte("[a.li.c+e@keybasers.de]@email")},
122		{EOF, []byte{}},
123	}
124	testLexer(t, "square brackets 1", s, expected)
125
126	s = "alice@twitter||email:[a.li.c+e@keybasers.de]"
127	expected = []Token{
128		{URL, []byte("alice@twitter")},
129		{OR, []byte("||")},
130		{URL, []byte("email:[a.li.c+e@keybasers.de]")},
131		{EOF, []byte{}},
132	}
133	testLexer(t, "square brackets 2", s, expected)
134
135	s = "alice@twitter||email://[a.li.c+e@keybasers.de]"
136	expected = []Token{
137		{URL, []byte("alice@twitter")},
138		{OR, []byte("||")},
139		{URL, []byte("email://[a.li.c+e@keybasers.de]")},
140		{EOF, []byte{}},
141	}
142	testLexer(t, "square brackets 3", s, expected)
143
144	// This is not a valid email, but not caught at the lexer stage,
145	// it's still supposed to generate URL token.
146	s = "twitter:ae,email:[a,e@keybasers.de]"
147	expected = []Token{
148		{URL, []byte("twitter:ae")},
149		{OR, []byte(",")},
150		{URL, []byte("email:[a,e@keybasers.de]")},
151		{EOF, []byte{}},
152	}
153	testLexer(t, "square brackets 4", s, expected)
154
155	// Same here:
156	s = "email:[],email://[]"
157	expected = []Token{
158		{URL, []byte("email:[]")},
159		{OR, []byte(",")},
160		{URL, []byte("email://[]")},
161		{EOF, []byte{}},
162	}
163	testLexer(t, "square brackets 5", s, expected)
164
165	// Weirdness
166	s = "[michal]@[keybase]"
167	expected = []Token{
168		{URL, []byte("[michal]@")},
169		{ERROR, []byte("[keybase]")},
170		{EOF, []byte{}},
171	}
172	testLexer(t, "square brackets 6", s, expected)
173}
174
175func TestParser1(t *testing.T) {
176	inp := "  aa ||   bb   && cc ||\n dd ||\n ee && ff || gg && (hh ||\nii)"
177	outp := "aa,bb+cc,dd,ee+ff,gg+(hh,ii)"
178	expr, err := AssertionParse(testAssertionContext{}, inp)
179	if err != nil {
180		t.Error(err)
181	} else if expr.String() != outp {
182		t.Errorf("Wrong parse result: %s v %s", expr.String(), outp)
183	}
184}
185
186func TestParser2(t *testing.T) {
187	inp := "  web://a.aa ||   http://b.bb   && dns://c.cc ||\n dd ||\n pgp:ee && reddit:foo || twitter:goo && (https:h.in ||\ndns:i.co)"
188	outp := "a.aa@web,b.bb@http+c.cc@dns,dd,ee@pgp+foo@reddit,goo@twitter+(h.in@https,i.co@dns)"
189	expr, err := AssertionParse(testAssertionContext{}, inp)
190	if err != nil {
191		t.Error(err)
192	} else if expr.String() != outp {
193		t.Errorf("Wrong parse result: %s v %s", expr.String(), outp)
194	}
195}
196
197func TestNormalization(t *testing.T) {
198	// Test moved to externals/ since it requires knowledge of social networks
199}
200
201type Pair struct {
202	k, v string
203}
204
205func TestParserFail1(t *testing.T) {
206	bads := []Pair{
207		{"aa ||", "Unexpected EOF parsing assertion"},
208		{"aa &&", "Unexpected EOF parsing assertion"},
209		{"(aa", "Unbalanced parentheses"},
210		{"aa && dns:", "Bad assertion, no value given (key=dns)"},
211		{"&& aa", "Unexpected token: &&"},
212		{"|| aa", "Unexpected token: ||"},
213		{"aa)", "Found junk at end of input: )"},
214		{"()", "Illegal parenthetical expression"},
215		{"a@pgp", "bad hex string: 'a'"},
216		{"aBCP@pgp", "bad hex string: 'abcp'"},
217		{"jj@pgp", "bad hex string: 'jj'"},
218		{"aa && |bb", "Syntax error when parsing: |bb"},
219	}
220
221	for _, bad := range bads {
222		expr, err := AssertionParse(testAssertionContext{}, bad.k)
223		if err == nil {
224			t.Errorf("Expected a parse error in %s (got %v)", bad, expr)
225		} else if err.Error() != bad.v {
226			t.Errorf("Got wrong error; wanted '%s', but got '%s'", bad.v, err)
227		}
228	}
229}
230
231func TestParseAssertionsWithReaders(t *testing.T) {
232	units := []struct {
233		input   string
234		writers string // expected writers (reserialized and comma-sep)
235		readers string // expected readers
236		error   string // expected error regex
237	}{
238		{
239			input:   "alice,bob&&bob@twitter",
240			writers: "alice,bob+bob@twitter",
241		},
242		{
243			input:   "alice,bob&&bob@twitter#char",
244			writers: "alice,bob+bob@twitter",
245			readers: "char",
246		},
247		{
248			input:   "alice#char,liz",
249			writers: "alice",
250			readers: "char,liz",
251		},
252		{
253			input:   "alice",
254			writers: "alice",
255		},
256		{
257			// doesn't dedup at the moment
258			input:   "alice#alice",
259			writers: "alice",
260			readers: "alice",
261		},
262		{
263			input:   "alice+github:alice",
264			writers: "alice+alice@github",
265		},
266		{
267			input:   "1f5DE74F4D4E8884775F2BF1514FC07220D4A61A@pgp,milessteele.com@https",
268			writers: "1f5de74f4d4e8884775f2bf1514fc07220d4a61a@pgp,milessteele.com@https",
269		},
270		{
271			input: "",
272			error: `^empty assertion$`,
273		},
274		{
275			input: "#char,liz",
276			error: `.*`,
277		},
278		{
279			input: "char||liz",
280			error: `disallowed.*OR`,
281		},
282	}
283
284	ser := func(exprs []AssertionExpression) string {
285		var strs []string
286		for _, expr := range exprs {
287			strs = append(strs, expr.String())
288		}
289		return strings.Join(strs, ",")
290	}
291
292	for i, unit := range units {
293		t.Logf("%v: %v", i, unit.input)
294		writers, readers, err := ParseAssertionsWithReaders(testAssertionContext{}, unit.input)
295		if len(unit.error) == 0 {
296			require.NoError(t, err)
297			require.Equal(t, unit.writers, ser(writers))
298			require.Equal(t, unit.readers, ser(readers))
299		} else {
300			require.Error(t, err)
301			require.Regexp(t, unit.error, err.Error())
302		}
303	}
304}
305