All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.spotify.scio.testing.BigtableMatchers.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019 Spotify AB.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.spotify.scio.testing

import com.google.bigtable.v2.Mutation
import com.google.bigtable.v2.Mutation.MutationCase
import com.google.protobuf.ByteString
import com.spotify.scio.values.SCollection

import org.scalatest.matchers.{MatchResult, Matcher}

/**
 * Trait with ScalaTest [[org.scalatest.matchers.Matcher Matcher]] s for
 * [[com.spotify.scio.values.SCollection SCollection]] s specific to Bigtable output.
 */
trait BigtableMatchers extends SCollectionMatchers {

  type BTRow = (ByteString, Iterable[Mutation])
  type BTCollection = SCollection[BTRow]

  /** Provide an implicit BT serializer for common cell value type String. */
  implicit def stringBTSerializer(s: String): ByteString =
    ByteString.copyFromUtf8(s)

  /** Check that the BT collection contains only the given keys, in any order. */
  def containRowKeys(expectedKeys: ByteString*): Matcher[BTCollection] =
    new Matcher[BTCollection] {
      override def apply(left: BTCollection): MatchResult =
        containInAnyOrder(expectedKeys).apply(left.keys)
    }

  /** Check that the BT collection contains only the given column families, unique, in any order. */
  def containColumnFamilies(expectedCFs: String*): Matcher[BTCollection] =
    new Matcher[BTCollection] {
      override def apply(left: BTCollection): MatchResult = {
        val foundCFs = left.flatMap { case (_, cells) =>
          cells.map(_.getSetCell.getFamilyName)
        }

        containInAnyOrder(expectedCFs).apply(foundCFs.distinct)
      }
    }

  /**
   * Check that the BT collection contains a cell with the given row key, column family, and
   * deserialized cell value. Column qualifier defaults to the same as column family.
   */
  def containSetCellValue[V](key: ByteString, cf: String, value: V)(implicit
    ser: V => ByteString
  ): Matcher[BTCollection] =
    containSetCellValue(key, cf, cf, value)

  /**
   * Check that the BT collection contains a cell with the given row key, column family, column
   * qualifier, and deserialized cell value.
   * @param key
   *   Row key the cell should be in
   * @param cf
   *   Column family the cell should have
   * @param cq
   *   Column qualifier the cell should have
   * @param value
   *   Deserialized value of the set cell
   * @param ser
   *   Serializer to convert value type V to ByteString for BT format
   * @tparam V
   *   Class of expected value
   */
  def containSetCellValue[V](key: ByteString, cf: String, cq: String, value: V)(implicit
    ser: V => ByteString
  ): Matcher[BTCollection] =
    new Matcher[BTCollection] {
      override def apply(left: BTCollection): MatchResult = {
        val flattenedRows = left.flatMap { case (rowKey, rowValue) =>
          rowValue.map { cell =>
            (
              rowKey,
              cell.getSetCell.getFamilyName,
              cell.getSetCell.getColumnQualifier,
              cell.getSetCell.getValue
            )
          }
        }

        containValue(
          (
            key,
            cf,
            ByteString.copyFromUtf8(cq),
            ser.apply(value)
          )
        ).apply(flattenedRows)
      }
    }

  /**
   * Check that the BT collection contains a cell with the given row key and enumerated
   * MutationCase, making no assumptions about the contents of the rest of the collection.
   */
  def containCellMutationCase[V](
    key: ByteString,
    mutation: MutationCase
  ): Matcher[BTCollection] =
    new Matcher[BTCollection] {
      override def apply(left: BTCollection): MatchResult = {
        val flattenedRows = left.flatMap { case (rowKey, rowValue) =>
          rowValue.map(cell => (rowKey, cell.getMutationCase.toString))
        }

        containValue((key, mutation.toString)).apply(flattenedRows)
      }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy