content: {-
    Copyright (c) Meta Platforms, Inc. and affiliates.
    All rights reserved.
  
    This source code is licensed under the BSD-style license found in the
    LICENSE file in the root directory of this source tree.
  -}
  
  {-# LANGUAGE QuasiQuotes #-}
  {-# LANGUAGE TypeApplications #-}
  module Angle.ErrorTest (main) where
  
  import Control.Exception
  import qualified Data.Text as Text
  import Test.HUnit
  
  import TestRunner
  import Util.String.Quasi
  
  import Glean.Init
  import Glean.Query.Thrift as Thrift
  import qualified Glean.Schema.Cxx1.Types as Cxx
  import qualified Glean.Schema.GleanTest.Types as Glean.Test
  import Glean.Types
  
  import TestDB
  
  main :: IO ()
  main = withUnitTest $ testRunner $ TestList
    [ TestLabel "error" angleErrorTests
    ]
  
  -- No need to re-run the error tests with paging, so we separate them out.
  angleErrorTests  :: Test
  angleErrorTests = dbTestCase $ \env repo -> do
    -- sum type: error to give multiple alternatives
    results <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate
      [s|
        glean.test.Predicate { sum_ = { d = _, c = _ } }
      |]
    print results
    assertBool "angle - sum - too many alts" $
      case results of
        Left (BadQuery s) -> "union type" `Text.isInfixOf` s
        _ -> False
  
    -- check that we get an error if there's a version mismatch between
    -- query and call site.
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate_0
      [s|
        glean.test.Predicate { }
      |]
    assertBool "angle - type error" $
      case r of
        Left BadQuery{} -> True
        _ -> False
  
    -- type error when using a type annotation on the lhs
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate_0
      [s|
        P where
        P = glean.test.Predicate { array_of_pred = X };
        glean.test.KitchenSink { nat = 42 } = X
      |]
    print r
    assertBool "angle - type error 2" $
      case r of
        Left (BadQuery s) -> "type error" `Text.isInfixOf` s
        _ -> False
  
    -- using the wrong predicate name is an error in a nested query
    r <- try $ runQuery_ env repo $ angle @Cxx.FunctionName
      [s|
        cxx1.FunctionName { name = sys.Blob "an".. }
      |]
    print r
    assertBool "angle - nested 3" $
      case r of
        Left (BadQuery x) -> "expected type: cxx1.Name" `Text.isInfixOf` x
        _ -> False
  
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate
      [s|
        N = cxx1.Name "abc";
        N[..]
      |]
    print r
    assertBool "angle - array generator type error" $
      case r of
        Left (BadQuery x) -> "type error" `Text.isInfixOf` x
        _ -> False
  
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate
      [s|
        unknown.predicate "abc"
      |]
    print r
    assertBool "angle - unknown predicate " $
      case r of
        Left (BadQuery x) -> "not in scope" `Text.isInfixOf` x
        _ -> False
  
    -- general type error
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate
      [s|
        cxx1.Name 3
      |]
    print r
    assertBool "angle - type error" $
      case r of
        Left (BadQuery x) -> "type error" `Text.isInfixOf` x
        _ -> False
  
    -- types of generators in sequence don't match
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate
      [s|
        (cxx1.Name "abc") | (cxx1.Type "def")
      |]
    print r
    assertBool "angle - mismatched generator types" $
      case r of
        Left (BadQuery x) ->
          "type error" `Text.isInfixOf` x
        _ -> False
  
    -- types of array elements don't match
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate
      [s|
        [A,B] where A = cxx1.Name "abc"; B = cxx1.Type "def"
      |]
    print r
    assertBool "angle - mismatched array elements" $
      case r of
        Left (BadQuery x) -> "type error" `Text.isInfixOf` x
        _ -> False
  
    r <- try $ runQuery_ env repo $ angle @Cxx.Name
      [s|
        _ = prim.length (_ : [string]); cxx1.Name "xyz"
      |]
    print r
    assertBool "angle - wildcard in expr" $
      case r of
        Left (BadQuery x) -> "unbound variable" `Text.isInfixOf` x
        _ -> False
  
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate
      [s|
        x = 1; glean.test.Predicate { nat = x }
      |]
    print r
    assertBool "angle - lowercase variable" $
      case r of
        Left (BadQuery x) -> "type error"
          `Text.isInfixOf` x
        _ -> False
  
    -- source location of error
    r <- try $ runQuery_ env repo $ angle @Glean.Test.Predicate
      [s|
        A where
          A = glean.test.Node _;
          B = glean.test.Edge _;
          A = B;
      |]
    print r
    assertBool "angle - variable location" $
      case r of
        Left (BadQuery x) -> "line 5, column 9" `Text.isInfixOf` x
        _ -> False
  
    r <- try $ runQuery_ env repo $ angleData @Nat "1 + never"
    print r
    assertBool
      "angle never - cannot be used in an expression" $
      case r of
        Left (BadQuery x) ->
          "cannot use 'never' in an expression" `Text.isInfixOf` x
        _ -> False
  
    r <- runQuery_ env repo $ angleData @Nat "A = [1,2][..]; never = A; A"
    print r
    assertEqual
      "angle - never does not match any result"
      r []
  
    r <- runQuery_ env repo $ angleData @Glean.Test.Predicate
      "A = never : glean.test.Predicate; A"
    print r
    assertEqual
      "angle - works for FactGenerators"
      r []
  
    -- a negated term has type unit
    r <- try $ runQuery_ env repo $ angleData @Nat "!1 = 2; 3"
    print r
    assertBool "angle - negation type" $
      case r of
        Left (BadQuery x) -> "type error" `Text.isInfixOf` x
        _ -> False
  
    -- unbound variables in a negated statement are errors
    r <- try $ runQuery_ env repo $ angleData @Nat "!(A + A)"
    print r
    assertBool "angle - negation unbound" $
      case r of
        Left (BadQuery x) ->
          "unbound variable: A" `Text.isInfixOf` x
        _ -> False
  