Skip to content

Commit 309c64c

Browse files
committed
add integration test
1 parent f9afefc commit 309c64c

12 files changed

Lines changed: 252 additions & 7 deletions

File tree

build.sbt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,20 @@ version := "0.0.1-SNAPSHOT"
44

55
scalaVersion := "2.11.8"
66

7-
val scalikejdbcVersion = "2.4.2"
7+
configs(IntegrationTest)
8+
9+
inConfig(IntegrationTest)(Defaults.itSettings)
10+
11+
val scalikejdbcVersion = "3.0.0-RC3"
812
val googleCloudVersion = "0.9.3-beta"
913

1014
libraryDependencies ++= Seq(
1115
"org.scalikejdbc" %% "scalikejdbc" % scalikejdbcVersion % "provided",
12-
"org.scalikejdbc" %% "scalikejdbc" % scalikejdbcVersion % "test",
16+
"org.scalikejdbc" %% "scalikejdbc" % scalikejdbcVersion % "it,test",
1317

1418
"com.google.cloud" % "google-cloud-bigquery" % googleCloudVersion % "provided",
15-
"com.google.cloud" % "google-cloud-bigquery" % googleCloudVersion % "test",
19+
"com.google.cloud" % "google-cloud-bigquery" % googleCloudVersion % "it,test",
1620

17-
"org.scalatest" %% "scalatest" % "3.0.1" % "test",
18-
"org.scalamock" %% "scalamock-scalatest-support" % "3.5.0" % "test"
21+
"org.scalatest" %% "scalatest" % "3.0.1" % "it,test",
22+
"org.scalamock" %% "scalamock-scalatest-support" % "3.5.0" % "it,test"
1923
)

src/it/resources/rows_post.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{"id":1,"body":"first post about jvm languages","posted_at":"2017-03-23 10:00:00.000000"}
2+
{"id":2,"body":"second post","posted_at":"2017-03-24 10:00:00.000000"}
3+
{"id":3,"body":"third post about lightweight languages","posted_at":"2017-03-25 10:00:00.000000"}

src/it/resources/rows_tag.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{"id":1,"post_id":1,"name":"java"}
2+
{"id":2,"post_id":1,"name":"scala"}
3+
{"id":3,"post_id":3,"name":"ruby"}
4+
{"id":4,"post_id":3,"name":"python"}
5+
{"id":5,"post_id":3,"name":"perl"}

src/it/resources/schema_post.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[
2+
{
3+
"mode": "required",
4+
"type": "INT64",
5+
"name": "id"
6+
},
7+
{
8+
"mode": "required",
9+
"type": "STRING",
10+
"name": "body"
11+
},
12+
{
13+
"mode": "required",
14+
"type": "TIMESTAMP",
15+
"name": "posted_at"
16+
}
17+
]

src/it/resources/schema_tag.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[
2+
{
3+
"mode": "required",
4+
"type": "INT64",
5+
"name": "id"
6+
},
7+
{
8+
"mode": "required",
9+
"type": "INT64",
10+
"name": "post_id"
11+
},
12+
{
13+
"mode": "required",
14+
"type": "STRING",
15+
"name": "name"
16+
}
17+
]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package scalikejdbc.bigquery
2+
3+
import java.io.FileInputStream
4+
5+
import com.google.auth.oauth2.GoogleCredentials
6+
import com.google.cloud.bigquery.{BigQueryOptions, BigQuery}
7+
8+
trait BigQueryFixture {
9+
10+
def projectId(): String = sys.env("GCLOUD_PROJECT")
11+
12+
def mkBigQuery(): BigQuery = {
13+
val jsonKeyFileLocation = sys.env("GCLOUD_SERVICE_KEY_LOCATION")
14+
val credentials = GoogleCredentials.fromStream(new FileInputStream(jsonKeyFileLocation))
15+
16+
BigQueryOptions.newBuilder()
17+
.setCredentials(credentials)
18+
.setProjectId(projectId())
19+
.build()
20+
.getService
21+
}
22+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package scalikejdbc.bigquery
2+
3+
import java.time.{ZoneId, Instant, ZonedDateTime}
4+
5+
import scalikejdbc._
6+
7+
case class PostId(value: Int) extends AnyVal
8+
9+
case class Post(
10+
id: PostId,
11+
body: String,
12+
postedAt: ZonedDateTime
13+
)
14+
15+
case class PostWithTags(
16+
post: Post,
17+
tags: Seq[Tag]
18+
)
19+
20+
object Post extends SQLSyntaxSupport[Post] {
21+
override val columns = Seq("id", "body", "posted_at")
22+
23+
implicit val postIdBinders: Binders[PostId] = Binders.int.xmap(PostId.apply, _.value)
24+
implicit val zonedDateTimeTypeBinder: TypeBinder[ZonedDateTime] =
25+
TypeBinder.jodaDateTime.map(t => ZonedDateTime.ofInstant(Instant.ofEpochMilli(t.getMillis), ZoneId.of("UTC")))
26+
27+
val p = this.syntax("p")
28+
29+
def apply(rs: WrappedResultSet): Post = apply(rs, p.resultName)
30+
def apply(rs: WrappedResultSet, rn: ResultName[Post]): Post = new Post(
31+
id = rs.get[PostId](rn.id),
32+
body = rs.get[String](rn.body),
33+
postedAt = rs.get[ZonedDateTime](rn.postedAt))
34+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package scalikejdbc.bigquery
2+
3+
import java.time.{ZonedDateTime, ZoneId}
4+
5+
import com.google.cloud.bigquery.DatasetId
6+
import org.scalatest.FlatSpec
7+
import scalikejdbc._, BqExtension._
8+
9+
class QueryDSLIntegration extends FlatSpec with BigQueryFixture {
10+
11+
//========================
12+
// Suppose that tables and rows like following exist in DataSet:"scalikejdbc_bigquery_integration".
13+
//
14+
// create table post (
15+
// id INT64,
16+
// body STRING,
17+
// posted_at TIMESTAMP,
18+
// );
19+
//
20+
// create table tag (
21+
// id INT64,
22+
// post_id INT64,
23+
// name STRING
24+
// );
25+
//
26+
// insert into post(id, body, posted_at) values (1, 'first post about jvm languages', '2017-03-23T10:00:00.000000+0000')
27+
// insert into tag(id, post_id, name) values (1, 1, 'java');
28+
// insert into tag(id, post_id, name) values (2, 1, 'scala');
29+
//
30+
// insert into post(id, body, posted_at) values (2, 'second post', '2017-03-24T10:00:00.000000+0000')
31+
//
32+
// insert into post(id, body, posted_at) values (3, 'third post about lightweight languages', '2017-03-25T10:00:00.000000+0000')
33+
// insert into tag(id, post_id, name) values (3, 3, 'ruby');
34+
// insert into tag(id, post_id, name) values (4, 3, 'python');
35+
// insert into tag(id, post_id, name) values (5, 3, 'perl');
36+
//
37+
//========================
38+
it should "work correctly on standard SQL" in {
39+
val bigQuery = mkBigQuery()
40+
val queryConfig = QueryConfig()
41+
42+
val dataset = DatasetId.of(projectId(), "scalikejdbc_bigquery_integration")
43+
44+
val executor = new QueryExecutor(bigQuery, queryConfig)
45+
46+
import Post.p, Tag.t, Post.postIdBinders
47+
val sub = SubQuery.syntax("sub").include(p)
48+
49+
val response = bq {
50+
select(sub.result.*, t.result.*).from(
51+
select(p.result.*).from(standardTableReference(dataset, Post.tableName, Some(p.tableAliasName)))
52+
.where.in(p.id, Seq(PostId(1), PostId(2), PostId(3)))
53+
.limit(3)
54+
.offset(0)
55+
.as(sub)
56+
)
57+
.leftJoin(standardTableReference(dataset, Tag.tableName, Some(t.tableAliasName)))
58+
.on(sub(p).id, t.postId)
59+
.orderBy(sub(p).id, t.id)
60+
}
61+
.one(Post(_, sub(p).resultName))
62+
.toMany(Tag.opt)
63+
.map(PostWithTags)
64+
.list
65+
.run(executor)
66+
67+
val result = response.result
68+
69+
assert(result.size == 3)
70+
71+
assert(result(0) == PostWithTags(
72+
Post(PostId(1), "first post about jvm languages", ZonedDateTime.of(2017, 3, 23, 10, 0, 0, 0, ZoneId.of("UTC"))),
73+
Seq(
74+
Tag(TagId(1), PostId(1), "java"),
75+
Tag(TagId(2), PostId(1), "scala")
76+
)
77+
))
78+
79+
assert(result(1) == PostWithTags(
80+
Post(PostId(2), "second post", ZonedDateTime.of(2017, 3, 24, 10, 0, 0, 0, ZoneId.of("UTC"))),
81+
Nil
82+
))
83+
84+
assert(result(2) == PostWithTags(
85+
Post(PostId(3), "third post about lightweight languages", ZonedDateTime.of(2017, 3, 25, 10, 0, 0, 0, ZoneId.of("UTC"))),
86+
Seq(
87+
Tag(TagId(3), PostId(3), "ruby"),
88+
Tag(TagId(4), PostId(3), "python"),
89+
Tag(TagId(5), PostId(3), "perl")
90+
)
91+
))
92+
}
93+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package scalikejdbc.bigquery
2+
3+
import scalikejdbc._
4+
5+
case class TagId(value: Int) extends AnyVal
6+
7+
case class Tag(
8+
id: TagId,
9+
postId: PostId,
10+
name: String
11+
)
12+
13+
object Tag extends SQLSyntaxSupport[Tag] {
14+
override val columns = Seq("id", "post_id", "name")
15+
16+
import Post.postIdBinders
17+
18+
implicit val tagIdBinders: Binders[TagId] = Binders.int.xmap(TagId.apply, _.value)
19+
20+
val t = this.syntax("t")
21+
22+
def apply(rs: WrappedResultSet): Tag = new Tag(
23+
id = rs.get[TagId](t.resultName.id),
24+
postId = rs.get[PostId](t.resultName.postId),
25+
name = rs.get[String](t.resultName.name)
26+
)
27+
def opt(rs: WrappedResultSet): Option[Tag] = rs.intOpt(t.resultName.id).map(_ => apply(rs))
28+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package scalikejdbc
2+
3+
import com.google.cloud.bigquery.DatasetId
4+
5+
object BqExtension {
6+
7+
def standardTableReference(
8+
datasetId: DatasetId,
9+
tableName: String,
10+
tableAliasName: Option[String] = None): TableAsAliasSQLSyntax = {
11+
12+
val tableReference = s"`${datasetId.getProject}.${datasetId.getDataset}.${tableName}`"
13+
14+
val withAlias = tableAliasName.fold(tableReference) { alias =>
15+
s"${tableReference} ${alias}"
16+
}
17+
18+
TableAsAliasSQLSyntax(withAlias)
19+
}
20+
}

0 commit comments

Comments
 (0)