cassandra_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. // Copyright (c) 2012 The gocql Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gocql
  5. import (
  6. "bytes"
  7. "flag"
  8. "reflect"
  9. "sort"
  10. "strings"
  11. "sync"
  12. "testing"
  13. "time"
  14. )
  15. var (
  16. flagCluster = flag.String("cluster", "127.0.0.1", "a comma-separated list of host:port tuples")
  17. flagProto = flag.Int("proto", 2, "protcol version")
  18. flagCQL = flag.String("cql", "3.0.0", "CQL version")
  19. )
  20. var initOnce sync.Once
  21. func createSession(t *testing.T) *Session {
  22. cluster := NewCluster(strings.Split(*flagCluster, ",")...)
  23. cluster.ProtoVersion = *flagProto
  24. cluster.CQLVersion = *flagCQL
  25. cluster.Authenticator = PasswordAuthenticator{
  26. Username: "cassandra",
  27. Password: "cassandra",
  28. }
  29. session, err := cluster.CreateSession()
  30. if err != nil {
  31. t.Fatal("createSession:", err)
  32. }
  33. initOnce.Do(func() {
  34. // Drop and re-create the keyspace once. Different tests should use their own
  35. // individual tables, but can assume that the table does not exist before.
  36. if err := session.Query(`DROP KEYSPACE gocql_test`).Exec(); err != nil {
  37. t.Log("drop keyspace:", err)
  38. }
  39. if err := session.Query(`CREATE KEYSPACE gocql_test
  40. WITH replication = {
  41. 'class' : 'SimpleStrategy',
  42. 'replication_factor' : 1
  43. }`).Exec(); err != nil {
  44. t.Fatal("create keyspace:", err)
  45. }
  46. })
  47. if err := session.Query(`USE gocql_test`).Exec(); err != nil {
  48. t.Fatal("createSession:", err)
  49. }
  50. return session
  51. }
  52. func TestEmptyHosts(t *testing.T) {
  53. cluster := NewCluster()
  54. if session, err := cluster.CreateSession(); err == nil {
  55. session.Close()
  56. t.Error("expected err, got nil")
  57. }
  58. }
  59. func TestCRUD(t *testing.T) {
  60. session := createSession(t)
  61. defer session.Close()
  62. if err := session.Query(`CREATE TABLE page (
  63. title varchar,
  64. revid timeuuid,
  65. body varchar,
  66. views bigint,
  67. protected boolean,
  68. modified timestamp,
  69. tags set<varchar>,
  70. attachments map<varchar, text>,
  71. PRIMARY KEY (title, revid)
  72. )`).Exec(); err != nil {
  73. t.Fatal("create table:", err)
  74. }
  75. for _, page := range pageTestData {
  76. if err := session.Query(`INSERT INTO page
  77. (title, revid, body, views, protected, modified, tags, attachments)
  78. VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
  79. page.Title, page.RevId, page.Body, page.Views, page.Protected,
  80. page.Modified, page.Tags, page.Attachments).Exec(); err != nil {
  81. t.Fatal("insert:", err)
  82. }
  83. }
  84. var count int
  85. if err := session.Query("SELECT COUNT(*) FROM page").Scan(&count); err != nil {
  86. t.Error("select count:", err)
  87. }
  88. if count != len(pageTestData) {
  89. t.Errorf("count: expected %d, got %d\n", len(pageTestData), count)
  90. }
  91. for _, original := range pageTestData {
  92. page := new(Page)
  93. err := session.Query(`SELECT title, revid, body, views, protected, modified,
  94. tags, attachments
  95. FROM page WHERE title = ? AND revid = ? LIMIT 1`,
  96. original.Title, original.RevId).Scan(&page.Title, &page.RevId,
  97. &page.Body, &page.Views, &page.Protected, &page.Modified, &page.Tags,
  98. &page.Attachments)
  99. if err != nil {
  100. t.Error("select page:", err)
  101. continue
  102. }
  103. sort.Sort(sort.StringSlice(page.Tags))
  104. sort.Sort(sort.StringSlice(original.Tags))
  105. if !reflect.DeepEqual(page, original) {
  106. t.Errorf("page: expected %#v, got %#v\n", original, page)
  107. }
  108. }
  109. }
  110. func TestTracing(t *testing.T) {
  111. session := createSession(t)
  112. defer session.Close()
  113. if err := session.Query(`CREATE TABLE trace (id int primary key)`).Exec(); err != nil {
  114. t.Fatal("create:", err)
  115. }
  116. buf := &bytes.Buffer{}
  117. trace := NewTraceWriter(session, buf)
  118. if err := session.Query(`INSERT INTO trace (id) VALUES (?)`, 42).Trace(trace).Exec(); err != nil {
  119. t.Error("insert:", err)
  120. } else if buf.Len() == 0 {
  121. t.Error("insert: failed to obtain any tracing")
  122. }
  123. buf.Reset()
  124. var value int
  125. if err := session.Query(`SELECT id FROM trace WHERE id = ?`, 42).Trace(trace).Scan(&value); err != nil {
  126. t.Error("select:", err)
  127. } else if value != 42 {
  128. t.Errorf("value: expected %d, got %d", 42, value)
  129. } else if buf.Len() == 0 {
  130. t.Error("select: failed to obtain any tracing")
  131. }
  132. }
  133. func TestPaging(t *testing.T) {
  134. t.Skip("Skip until https://github.com/gocql/gocql/issues/110 is resolved")
  135. if *flagProto == 1 {
  136. t.Skip("Paging not supported. Please use Cassandra >= 2.0")
  137. }
  138. session := createSession(t)
  139. defer session.Close()
  140. if err := session.Query("CREATE TABLE large (id int primary key)").Exec(); err != nil {
  141. t.Fatal("create table:", err)
  142. }
  143. for i := 0; i < 100; i++ {
  144. if err := session.Query("INSERT INTO large (id) VALUES (?)", i).Exec(); err != nil {
  145. t.Fatal("insert:", err)
  146. }
  147. }
  148. iter := session.Query("SELECT id FROM large").PageSize(10).Iter()
  149. var id int
  150. count := 0
  151. for iter.Scan(&id) {
  152. count++
  153. }
  154. if err := iter.Close(); err != nil {
  155. t.Fatal("close:", err)
  156. }
  157. if count != 100 {
  158. t.Fatalf("expected %d, got %d", 100, count)
  159. }
  160. }
  161. func TestCAS(t *testing.T) {
  162. if *flagProto == 1 {
  163. t.Skip("lightweight transactions not supported. Please use Cassandra >= 2.0")
  164. }
  165. session := createSession(t)
  166. defer session.Close()
  167. if err := session.Query(`CREATE TABLE cas_table (
  168. title varchar,
  169. revid timeuuid,
  170. PRIMARY KEY (title, revid)
  171. )`).Exec(); err != nil {
  172. t.Fatal("create:", err)
  173. }
  174. title, revid := "baz", TimeUUID()
  175. var titleCAS string
  176. var revidCAS UUID
  177. if applied, err := session.Query(`INSERT INTO cas_table (title, revid)
  178. VALUES (?, ?) IF NOT EXISTS`,
  179. title, revid).ScanCAS(&titleCAS, &revidCAS); err != nil {
  180. t.Fatal("insert:", err)
  181. } else if !applied {
  182. t.Fatal("insert should have been applied")
  183. }
  184. if applied, err := session.Query(`INSERT INTO cas_table (title, revid)
  185. VALUES (?, ?) IF NOT EXISTS`,
  186. title, revid).ScanCAS(&titleCAS, &revidCAS); err != nil {
  187. t.Fatal("insert:", err)
  188. } else if applied {
  189. t.Fatal("insert should not have been applied")
  190. } else if title != titleCAS || revid != revidCAS {
  191. t.Fatalf("expected %s/%v but got %s/%v", title, revid, titleCAS, revidCAS)
  192. }
  193. }
  194. func TestBatch(t *testing.T) {
  195. if *flagProto == 1 {
  196. t.Skip("atomic batches not supported. Please use Cassandra >= 2.0")
  197. }
  198. session := createSession(t)
  199. defer session.Close()
  200. if err := session.Query(`CREATE TABLE batch_table (id int primary key)`).Exec(); err != nil {
  201. t.Fatal("create table:", err)
  202. }
  203. batch := NewBatch(LoggedBatch)
  204. for i := 0; i < 100; i++ {
  205. batch.Query(`INSERT INTO batch_table (id) VALUES (?)`, i)
  206. }
  207. if err := session.ExecuteBatch(batch); err != nil {
  208. t.Fatal("execute batch:", err)
  209. }
  210. count := 0
  211. if err := session.Query(`SELECT COUNT(*) FROM batch_table`).Scan(&count); err != nil {
  212. t.Fatal("select count:", err)
  213. } else if count != 100 {
  214. t.Fatalf("count: expected %d, got %d\n", 100, count)
  215. }
  216. }
  217. // TestBatchLimit tests gocql to make sure batch operations larger than the maximum
  218. // statement limit are not submitted to a cassandra node.
  219. func TestBatchLimit(t *testing.T) {
  220. if *flagProto == 1 {
  221. t.Skip("atomic batches not supported. Please use Cassandra >= 2.0")
  222. }
  223. session := createSession(t)
  224. defer session.Close()
  225. if err := session.Query(`CREATE TABLE batch_table2 (id int primary key)`).Exec(); err != nil {
  226. t.Fatal("create table:", err)
  227. }
  228. batch := NewBatch(LoggedBatch)
  229. for i := 0; i < 65537; i++ {
  230. batch.Query(`INSERT INTO batch_table2 (id) VALUES (?)`, i)
  231. }
  232. if err := session.ExecuteBatch(batch); err != ErrTooManyStmts {
  233. t.Fatal("gocql attempted to execute a batch larger than the support limit of statements.")
  234. }
  235. }
  236. // TestCreateSessionTimeout tests to make sure the CreateSession function timeouts out correctly
  237. // and prevents an infinite loop of connection retries.
  238. func TestCreateSessionTimeout(t *testing.T) {
  239. go func() {
  240. <-time.After(2 * time.Second)
  241. t.Fatal("no startup timeout")
  242. }()
  243. c := NewCluster("127.0.0.1:1")
  244. c.StartupTimeout = 1 * time.Second
  245. _, err := c.CreateSession()
  246. if err == nil {
  247. t.Fatal("expected ErrNoConncetions, but no error was returned.")
  248. }
  249. if err != ErrNoConnections {
  250. t.Fatal("expected ErrNoConnections, but received %v", err)
  251. }
  252. }
  253. type Page struct {
  254. Title string
  255. RevId UUID
  256. Body string
  257. Views int64
  258. Protected bool
  259. Modified time.Time
  260. Tags []string
  261. Attachments map[string]Attachment
  262. }
  263. type Attachment []byte
  264. var pageTestData = []*Page{
  265. &Page{
  266. Title: "Frontpage",
  267. RevId: TimeUUID(),
  268. Body: "Welcome to this wiki page!",
  269. Modified: time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC),
  270. Tags: []string{"start", "important", "test"},
  271. Attachments: map[string]Attachment{
  272. "logo": Attachment("\x00company logo\x00"),
  273. "favicon": Attachment("favicon.ico"),
  274. },
  275. },
  276. &Page{
  277. Title: "Foobar",
  278. RevId: TimeUUID(),
  279. Body: "foo::Foo f = new foo::Foo(foo::Foo::INIT);",
  280. Modified: time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC),
  281. },
  282. }
  283. func TestSliceMap(t *testing.T) {
  284. session := createSession(t)
  285. defer session.Close()
  286. if err := session.Query(`CREATE TABLE slice_map_table (
  287. testuuid timeuuid PRIMARY KEY,
  288. testvarchar varchar,
  289. testbigint bigint,
  290. testblob blob,
  291. testbool boolean,
  292. testfloat float,
  293. testdouble double,
  294. testint int,
  295. testset set<int>,
  296. testmap map<varchar, varchar>
  297. )`).Exec(); err != nil {
  298. t.Fatal("create table:", err)
  299. }
  300. m := make(map[string]interface{})
  301. m["testuuid"] = TimeUUID()
  302. m["testvarchar"] = "Test VarChar"
  303. m["testbigint"] = time.Now().Unix()
  304. m["testblob"] = []byte("test blob")
  305. m["testbool"] = true
  306. m["testfloat"] = float32(4.564)
  307. m["testdouble"] = float64(4.815162342)
  308. m["testint"] = 2343
  309. m["testset"] = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
  310. m["testmap"] = map[string]string{"field1": "val1", "field2": "val2", "field3": "val3"}
  311. sliceMap := []map[string]interface{}{m}
  312. if err := session.Query(`INSERT INTO slice_map_table (testuuid, testvarchar, testbigint, testblob, testbool, testfloat, testdouble, testint, testset, testmap) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
  313. m["testuuid"], m["testvarchar"], m["testbigint"], m["testblob"], m["testbool"], m["testfloat"], m["testdouble"], m["testint"], m["testset"], m["testmap"]).Exec(); err != nil {
  314. t.Fatal("insert:", err)
  315. }
  316. if returned, retErr := session.Query(`SELECT * FROM slice_map_table`).Iter().SliceMap(); retErr != nil {
  317. t.Fatal("select:", retErr)
  318. } else {
  319. if sliceMap[0]["testuuid"] != returned[0]["testuuid"] {
  320. t.Fatal("returned testuuid did not match")
  321. }
  322. if sliceMap[0]["testvarchar"] != returned[0]["testvarchar"] {
  323. t.Fatal("returned testvarchar did not match")
  324. }
  325. if sliceMap[0]["testbigint"] != returned[0]["testbigint"] {
  326. t.Fatal("returned testbigint did not match")
  327. }
  328. if !reflect.DeepEqual(sliceMap[0]["testblob"], returned[0]["testblob"]) {
  329. t.Fatal("returned testblob did not match")
  330. }
  331. if sliceMap[0]["testbool"] != returned[0]["testbool"] {
  332. t.Fatal("returned testbool did not match")
  333. }
  334. if sliceMap[0]["testfloat"] != returned[0]["testfloat"] {
  335. t.Fatal("returned testfloat did not match")
  336. }
  337. if sliceMap[0]["testdouble"] != returned[0]["testdouble"] {
  338. t.Fatal("returned testdouble did not match")
  339. }
  340. if sliceMap[0]["testint"] != returned[0]["testint"] {
  341. t.Fatal("returned testint did not match")
  342. }
  343. if !reflect.DeepEqual(sliceMap[0]["testset"], returned[0]["testset"]) {
  344. t.Fatal("returned testset did not match")
  345. }
  346. if !reflect.DeepEqual(sliceMap[0]["testmap"], returned[0]["testmap"]) {
  347. t.Fatal("returned testmap did not match")
  348. }
  349. }
  350. // Test for MapScan()
  351. testMap := make(map[string]interface{})
  352. if !session.Query(`SELECT * FROM slice_map_table`).Iter().MapScan(testMap) {
  353. t.Fatal("MapScan failed to work with one row")
  354. }
  355. if sliceMap[0]["testuuid"] != testMap["testuuid"] {
  356. t.Fatal("returned testuuid did not match")
  357. }
  358. if sliceMap[0]["testvarchar"] != testMap["testvarchar"] {
  359. t.Fatal("returned testvarchar did not match")
  360. }
  361. if sliceMap[0]["testbigint"] != testMap["testbigint"] {
  362. t.Fatal("returned testbigint did not match")
  363. }
  364. if !reflect.DeepEqual(sliceMap[0]["testblob"], testMap["testblob"]) {
  365. t.Fatal("returned testblob did not match")
  366. }
  367. if sliceMap[0]["testbool"] != testMap["testbool"] {
  368. t.Fatal("returned testbool did not match")
  369. }
  370. if sliceMap[0]["testfloat"] != testMap["testfloat"] {
  371. t.Fatal("returned testfloat did not match")
  372. }
  373. if sliceMap[0]["testdouble"] != testMap["testdouble"] {
  374. t.Fatal("returned testdouble did not match")
  375. }
  376. if sliceMap[0]["testint"] != testMap["testint"] {
  377. t.Fatal("returned testint did not match")
  378. }
  379. if !reflect.DeepEqual(sliceMap[0]["testset"], testMap["testset"]) {
  380. t.Fatal("returned testset did not match")
  381. }
  382. if !reflect.DeepEqual(sliceMap[0]["testmap"], testMap["testmap"]) {
  383. t.Fatal("returned testmap did not match")
  384. }
  385. }
  386. func TestScanWithNilArguments(t *testing.T) {
  387. session := createSession(t)
  388. defer session.Close()
  389. if err := session.Query(`CREATE TABLE scan_with_nil_arguments (
  390. foo varchar,
  391. bar int,
  392. PRIMARY KEY (foo, bar)
  393. )`).Exec(); err != nil {
  394. t.Fatal("create:", err)
  395. }
  396. for i := 1; i <= 20; i++ {
  397. if err := session.Query("INSERT INTO scan_with_nil_arguments (foo, bar) VALUES (?, ?)",
  398. "squares", i*i).Exec(); err != nil {
  399. t.Fatal("insert:", err)
  400. }
  401. }
  402. iter := session.Query("SELECT * FROM scan_with_nil_arguments WHERE foo = ?", "squares").Iter()
  403. var n int
  404. count := 0
  405. for iter.Scan(nil, &n) {
  406. count += n
  407. }
  408. if err := iter.Close(); err != nil {
  409. t.Fatal("close:", err)
  410. }
  411. if count != 2870 {
  412. t.Fatalf("expected %d, got %d", 2870, count)
  413. }
  414. }
  415. func TestScanCASWithNilArguments(t *testing.T) {
  416. if *flagProto == 1 {
  417. t.Skip("lightweight transactions not supported. Please use Cassandra >= 2.0")
  418. }
  419. session := createSession(t)
  420. defer session.Close()
  421. if err := session.Query(`CREATE TABLE scan_cas_with_nil_arguments (
  422. foo varchar,
  423. bar varchar,
  424. PRIMARY KEY (foo, bar)
  425. )`).Exec(); err != nil {
  426. t.Fatal("create:", err)
  427. }
  428. foo := "baz"
  429. var cas string
  430. if applied, err := session.Query(`INSERT INTO scan_cas_with_nil_arguments (foo, bar)
  431. VALUES (?, ?) IF NOT EXISTS`,
  432. foo, foo).ScanCAS(nil, nil); err != nil {
  433. t.Fatal("insert:", err)
  434. } else if !applied {
  435. t.Fatal("insert should have been applied")
  436. }
  437. if applied, err := session.Query(`INSERT INTO scan_cas_with_nil_arguments (foo, bar)
  438. VALUES (?, ?) IF NOT EXISTS`,
  439. foo, foo).ScanCAS(&cas, nil); err != nil {
  440. t.Fatal("insert:", err)
  441. } else if applied {
  442. t.Fatal("insert should not have been applied")
  443. } else if foo != cas {
  444. t.Fatalf("expected %v but got %v", foo, cas)
  445. }
  446. if applied, err := session.Query(`INSERT INTO scan_cas_with_nil_arguments (foo, bar)
  447. VALUES (?, ?) IF NOT EXISTS`,
  448. foo, foo).ScanCAS(nil, &cas); err != nil {
  449. t.Fatal("insert:", err)
  450. } else if applied {
  451. t.Fatal("insert should not have been applied")
  452. } else if foo != cas {
  453. t.Fatalf("expected %v but got %v", foo, cas)
  454. }
  455. }