#include <bits/stdc++.h>
using namespace std;
template <typename T, typename U>
std::istream& operator>>(std::istream& i, pair<T, U>& p) {
  i >> p.first >> p.second;
  return i;
}
template <typename T>
std::istream& operator>>(std::istream& i, vector<T>& t) {
  for (auto& v : t) {
    i >> v;
  }
  return i;
}
template <typename T, typename U>
std::ostream& operator<<(std::ostream& o, const pair<T, U>& p) {
  o << p.first << ' ' << p.second;
  return o;
}
template <typename T>
std::ostream& operator<<(std::ostream& o, const vector<T>& t) {
  if (t.empty()) o << '\n';
  for (size_t i = 0; i < t.size(); ++i) {
    o << t[i] << " \n"[i == t.size() - 1];
  }
  return o;
}
template <typename T>
using minheap = priority_queue<T, vector<T>, greater<T>>;
template <typename T>
using maxheap = priority_queue<T, vector<T>, less<T>>;
unsigned int logceil(long long first) {
  return first ? 8 * sizeof(long long) - __builtin_clzll(first) : 0;
}
namespace std {
template <typename T, typename U>
struct hash<pair<T, U>> {
  hash<T> t;
  hash<U> u;
  size_t operator()(const pair<T, U>& p) const {
    return t(p.first) ^ (u(p.second) << 7);
  }
};
}  // namespace std
template <typename T, typename F>
T bsh(T l, T h, const F& f) {
  T r = -1, m;
  while (l <= h) {
    m = (l + h) / 2;
    if (f(m)) {
      l = m + 1;
      r = m;
    } else {
      h = m - 1;
    }
  }
  return r;
}
template <typename F>
double bshd(double l, double h, const F& f, double p = 1e-9) {
  unsigned int r = 3 + (unsigned int)log2((h - l) / p);
  while (r--) {
    double m = (l + h) / 2;
    if (f(m)) {
      l = m;
    } else {
      h = m;
    }
  }
  return (l + h) / 2;
}
template <typename T, typename F>
T bsl(T l, T h, const F& f) {
  T r = -1, m;
  while (l <= h) {
    m = (l + h) / 2;
    if (f(m)) {
      h = m - 1;
      r = m;
    } else {
      l = m + 1;
    }
  }
  return r;
}
template <typename F>
double bsld(double l, double h, const F& f, double p = 1e-9) {
  unsigned int r = 3 + (unsigned int)log2((h - l) / p);
  while (r--) {
    double m = (l + h) / 2;
    if (f(m)) {
      h = m;
    } else {
      l = m;
    }
  }
  return (l + h) / 2;
}
template <typename T>
T gcd(T a, T b) {
  if (a < b) swap(a, b);
  return b ? gcd(b, a % b) : a;
}
template <typename T>
class vector2 : public vector<vector<T>> {
 public:
  vector2() {}
  vector2(size_t a, size_t b, T t = T())
      : vector<vector<T>>(a, vector<T>(b, t)) {}
};
template <typename T>
class vector3 : public vector<vector2<T>> {
 public:
  vector3() {}
  vector3(size_t a, size_t b, size_t c, T t = T())
      : vector<vector2<T>>(a, vector2<T>(b, c, t)) {}
};
template <typename T>
class vector4 : public vector<vector3<T>> {
 public:
  vector4() {}
  vector4(size_t a, size_t b, size_t c, size_t d, T t = T())
      : vector<vector3<T>>(a, vector3<T>(b, c, d, t)) {}
};
template <typename T>
class vector5 : public vector<vector4<T>> {
 public:
  vector5() {}
  vector5(size_t a, size_t b, size_t c, size_t d, size_t e, T t = T())
      : vector<vector4<T>>(a, vector4<T>(b, c, d, e, t)) {}
};
auto seed = chrono::high_resolution_clock::now().time_since_epoch().count();
std::mt19937 rng(seed);
using random_int = std::uniform_int_distribution<int>;
using random_ll = std::uniform_int_distribution<long long>;
struct TimeLimit {
  TimeLimit(double seconds) : s(seconds - 0.2), last(clock()), mx(0) {}
  operator bool() const {
    clock_t cur = clock();
    mx = max(mx, cur - last);
    last = cur;
    return clock() < s * CLOCKS_PER_SEC - 1.5 * mx;
  }
  double s;
  mutable clock_t last, mx;
};
template <typename F>
struct NoOp {
  void setup(unsigned int, F) {}
  void op(F& p, F n, unsigned int, unsigned int) { p = n; }
  void down(F&, F&, F&, unsigned int, unsigned int) {}
};
template <typename F, typename SetOp, typename PowerOp>
struct Lazy {
  void setup(unsigned int s, F def) {
    this->def = def;
    this->s = s;
    L = new F[s]();
    fill(L, L + s, def);
  }
  void down(F& u, F& l, F& r, unsigned int i, unsigned int s) {
    op(l, L[i], i << 1, s >> 1);
    op(r, L[i], i << 1 | 1, s >> 1);
    L[i] = def;
  }
  void op(F& p, F n, unsigned int i, unsigned int s) {
    p = sop(p, pop(n, s));
    if (i < this->s) this->L[i] = sop(this->L[i], n);
  }
  SetOp sop;
  PowerOp pop;
  F* L;
  unsigned int s;
  F def;
};
template <typename F, typename CombineOp, typename ModifyOp = NoOp<F>>
struct SegTree {
  void setup(unsigned int s, F def) {
    n = 1 << logceil(s);
    T = vector<F>(2 * n, def);
    for (unsigned int i = n - 1; i > 0; i--)
      T[i] = cop(T[i << 1], T[i << 1 | 1]);
    mop.setup(2 * n, def);
  }
  void setup(vector<F>& data, F def = F(), F def2 = F()) {
    n = 1 << logceil(data.size());
    T = vector<F>(2 * n, def);
    copy(data.begin(), data.end(), T.begin() + n);
    for (unsigned int i = n - 1; i > 0; i--)
      T[i] = cop(T[i << 1], T[i << 1 | 1]);
    mop.setup(2 * n, def2);
  }
  inline void put(unsigned int first, F n) { put(first, first, n); }
  inline void put(unsigned int from, unsigned int to, F v) {
    put2(from, to + 1, v, 1, n);
  }
  inline F get(unsigned int first) { return get(first, first); }
  inline F get(unsigned int from, unsigned int to) {
    return get2(from, to + 1, 1, n);
  }
  void put2(unsigned int from, unsigned int to, F v, unsigned int i,
            unsigned int s) {
    if (from == 0 && to == s) {
      mop.op(T[i], v, i, s);
      return;
    }
    mop.down(T[i], T[i << 1], T[i << 1 | 1], i, s);
    s >>= 1;
    i <<= 1;
    if (to <= s) {
      put2(from, to, v, i, s);
    } else if (from >= s) {
      put2(from - s, to - s, v, i | 1, s);
    } else {
      put2(from, s, v, i, s);
      put2(0, to - s, v, i | 1, s);
    }
    T[i >> 1] = cop(T[i], T[i | 1]);
  }
  F get2(unsigned int from, unsigned int to, unsigned int i, unsigned int s) {
    while (true) {
      if (from == 0 && to == s) return T[i];
      mop.down(T[i], T[i << 1], T[i << 1 | 1], i, s);
      s >>= 1;
      i <<= 1;
      if (to > s) {
        to -= s;
        if (from >= s) {
          from -= s;
          i |= 1;
        } else
          return cop(get2(from, s, i, s), get2(0, to, i | 1, s));
      }
    }
  }
  unsigned int n;
  vector<F> T;
  CombineOp cop;
  ModifyOp mop;
};
template <typename F>
struct AddOp {
  F operator()(F a, F b) { return a + b; }
};
template <typename F>
struct MinOp {
  F operator()(F a, F b) { return std::min(a, b); }
};
template <typename F>
struct MaxOp {
  F operator()(F a, F b) { return std::max(a, b); }
};
template <typename F>
struct MultiplyOp {
  F operator()(F a, F b) { return a * b; }
};
template <typename F>
struct MultOp {
  F operator()(F a, unsigned int b) { return a * b; }
};
template <typename F>
struct IdempOp {
  F operator()(F a, unsigned int b) { return a; }
};
template <typename F>
struct InverseOp {
  F operator()(F a, F b) { return b ? b - a : a; }
};
template <typename T>
using AddSumTree = SegTree<T, AddOp<T>, Lazy<T, AddOp<T>, MultOp<T>>>;
template <typename T>
using AddMaxTree = SegTree<T, MaxOp<T>, Lazy<T, AddOp<T>, IdempOp<T>>>;
template <typename T>
using AddMinTree = SegTree<T, MinOp<T>, Lazy<T, AddOp<T>, IdempOp<T>>>;
template <typename T>
using AssignMinTree = SegTree<T, MinOp<T>, Lazy<T, MinOp<T>, IdempOp<T>>>;
template <typename T>
using AssignMaxTree = SegTree<T, MaxOp<T>, Lazy<T, MaxOp<T>, IdempOp<T>>>;
template <typename T>
using XorTree = SegTree<T, AddOp<T>, Lazy<T, InverseOp<T>, MultOp<T>>>;
template <typename T>
using SetMinTree = SegTree<T, MinOp<T>>;
template <typename T>
using SetMaxTree = SegTree<T, MaxOp<T>>;
template <typename T>
using SetMulTree = SegTree<T, MultiplyOp<T>>;
struct TreeX {
  explicit TreeX(int N = 1) : N(N), M(0), logN(logceil(N)), _root(0), E(N) {}
  void addEdge(int u, int v) {
    ++M;
    E[u].push_back(v);
    E[v].push_back(u);
  }
  int la(int u, int depth) { return _la(u, depth); }
  inline int lca(int u, int v) { return _lca(u, v); }
  int parent(int u) { return P[u]; }
  void setRoot(int r) { _root = r; }
  int root() { return _root; }
  const vector<int>& neighbors(int u) const { return E[u]; }
  int depth(int u) const { return D[u]; }
  bool isAncestor(int top, int bottom) { return lca(top, bottom) == top; }
  bool sameBranch(int top, int bottom) {
    int l = lca(top, bottom);
    return l == top || l == bottom;
  }
  int _la(int u, int d) const {
    for (int i = 0; i < logN && u != -1; ++i) {
      if (d & (1 << i)) u = PP[i][u];
    }
    return u;
  }
  int _lca(int u, int v) {
    if (D[u] < D[v]) {
      swap(u, v);
    }
    u = _la(u, D[u] - D[v]);
    for (int i = logN - 1; i >= 0; --i)
      if (PP[i][u] != PP[i][v]) {
        u = PP[i][u];
        v = PP[i][v];
      }
    return u == v ? u : PP[0][v];
  }
  inline void calcParents() {
    assert(M == N - 1);
    if (P.size() == 0) {
      P.resize(N);
      D.resize(N);
      dfs(_root, -1, 0);
    }
  }
  void dfs(int u, int v, int d) {
    P[u] = v;
    D[u] = d;
    for (int w : E[u])
      if (v != w) dfs(w, u, d + 1);
  }
  inline void calcLA() {
    calcParents();
    if (PP.size() == 0) {
      PP = vector2<int>(logN, N, -1);
      for (int i = 0; i < N; ++i) {
        PP[0][i] = P[i];
      }
      for (int i = 1; i < logN; ++i)
        for (int j = 0; j < N; ++j)
          if (PP[i - 1][j] != -1) PP[i][j] = PP[i - 1][PP[i - 1][j]];
    }
  }
  int N, M, logN, _root;
  vector<vector<int>> E;
  vector<int> P, D;
  vector2<int> PP;
};
template <typename EdgeType>
class DFSOrder {
 public:
  DFSOrder(const vector<vector<EdgeType>>& _E)
      : T(0), N(_E.size()), E(_E), RevEnter(N), Enter(N), Exit(N) {
    dfs(0, -1);
  }
  vector<vector<EdgeType>> linearize() {
    vector<vector<EdgeType>> F(N);
    for (int i = 0; i < N; ++i) {
      for (auto v : E[i]) {
        F[conv(i)].push_back(conv(v));
      }
    }
    return F;
  }
  void dfs(int u, int p) {
    RevEnter[T] = u;
    Enter[u] = T++;
    for (auto v : E[u])
      if (t(v) != p) dfs(t(v), u);
    Exit[u] = T;
  }
  inline int t(const int& e) const { return e; }
  template <typename U>
  inline int t(const pair<int, U>& e) const {
    return e.first;
  }
  inline int conv(const int& e) const { return Enter[e]; }
  template <typename U>
  inline pair<int, U> conv(const pair<int, U>& e) const {
    return {Enter[e.first], e.second};
  }
  const vector<int>& revEnter() const { return RevEnter; }
  const vector<int>& enter() const { return Enter; }
  const vector<int>& exit() const { return Exit; }
  int T, N;
  const vector<vector<EdgeType>>& E;
  vector<int> Enter, Exit, RevEnter;
};
template <typename EdgeType>
class CentroidDecomposition {
 public:
  CentroidDecomposition(const vector<vector<EdgeType>>& E) : E(E) {}
  const vector<int>& findCentroids() {
    DFSOrder<EdgeType> DFS(E);
    auto EF = DFS.linearize();
    N = E.size();
    U.assign(N, -1);
    int L = 0;
    for (int j = 0; j < N;) {
      for (int i = N - 1; i >= 0; --i) {
        if (U[i] >= 0) continue;
        U[i] = -1;
        bool root = true;
        for (auto v : EF[i]) {
          if (U[t(v)] < 0) {
            if (t(v) > i)
              U[i] += U[t(v)];
            else
              root = false;
          }
        }
        if (root) {
          int n = -U[i], u = i, p = -1;
          while (true) {
            int s = n + U[u];
            for (auto v : EF[u])
              if (t(v) != p && U[t(v)] < 0) s = max(s, -U[t(v)]);
            if (2 * s <= n) {
              U[u] = j++;
              break;
            } else {
              for (auto v : EF[u])
                if (t(v) != p && -U[t(v)] > n / 2) {
                  p = u;
                  u = t(v);
                  break;
                }
            }
          }
        }
      }
    }
    vector<int> Ans(N);
    for (int i = 0; i < N; ++i) Ans[i] = U[DFS.enter()[i]];
    swap(Ans, U);
    return U;
  }
  inline int t(const int& e) const { return e; }
  template <typename U>
  inline int t(const pair<int, U>& e) const {
    return e.first;
  }
  int N;
  const vector<vector<EdgeType>>& E;
  vector<int> U;
};
struct Bipartite {
  explicit Bipartite(unsigned int A, unsigned int B)
      : A(A), B(B), N(A + B), ms(-1), H(N, -1), used(A) {}
  void addEdge(int u, int v) {
    G.push_back(v);
    P.push_back(H[u]);
    H[u] = (unsigned int)G.size() - 1;
  }
  unsigned int matchingSize() {
    calcMatching();
    return ms;
  }
  int matched(int v) {
    calcMatching();
    return M[v];
  }
  vector<int> cover() {
    calcCover();
    vector<int> C;
    for (int i = 0; i < N; ++i) {
      if (Z[i] != (i < A)) C.push_back(i);
    }
    return C;
  }
  vector<int> indep() {
    calcCover();
    vector<int> I;
    for (int i = 0; i < N; ++i) {
      if (Z[i] == (i < A)) I.push_back(i);
    }
    return I;
  }
  void konigDfs(int u) {
    if (Z[u]) {
      return;
    }
    Z[u] = true;
    if (u < A) {
      for (unsigned int p = H[u]; p != -1; p = P[p]) {
        unsigned int v = G[p];
        if (v != M[u]) konigDfs(v);
      }
    } else if (M[u] != -1)
      konigDfs(M[u]);
  }
  bool matchingDfs(int u) {
    if (used[u]) return false;
    used[u] = true;
    for (unsigned int p = H[u]; p != -1; p = P[p]) {
      unsigned int v = G[p];
      if (M[v] == -1) {
        M[u] = v;
        M[v] = u;
        ++ms;
        return true;
      }
    }
    for (unsigned int p = H[u]; p != -1; p = P[p]) {
      unsigned int v = G[p];
      if (matchingDfs(M[v])) {
        M[u] = v;
        M[v] = u;
        return true;
      }
    }
    return false;
  }
  inline void calcMatching() {
    if (ms != -1) {
      return;
    }
    M = vector<unsigned int>(N, -1);
    size_t cur;
    ms = 0;
    do {
      cur = ms;
      for (int i = 0; i < A; ++i) used[i] = false;
      for (int i = 0; i < A; ++i)
        if (M[i] == -1) matchingDfs(i);
    } while (cur != ms);
  }
  inline void calcCover() {
    if (Z.size() == N) {
      return;
    }
    calcMatching();
    Z = vector<bool>(N, false);
    for (int i = 0; i < A; ++i)
      if (M[i] == -1) konigDfs(i);
  }
  unsigned int N, A, B, ms;
  vector<unsigned int> M, H, G, P;
  vector<bool> Z, used;
};
struct DirectedGraph {
  explicit DirectedGraph(int N) : N(N), E(N) {}
  void addEdge(int u, int v) { E[u].push_back({v}); }
  int stronglyConnectedComponent(int v) {
    calcStronglyConnectedComponents();
    return SCC[v];
  }
  bool stronglyConnected(int u, int v) {
    calcStronglyConnectedComponents();
    return SCC[u] == SCC[v];
  }
  const vector<int>& stronglyConnectedPermutation() {
    calcStronglyConnectedComponents();
    return O;
  }
  inline void calcReverseEdges() {
    if (ER.size() == N) {
      return;
    }
    ER.resize(N);
    for (int i = 0; i < N; ++i)
      for (int u : E[i]) ER[u].push_back(i);
  }
  inline void calcStronglyConnectedComponents() {
    if (SCC.size() == N) {
      return;
    }
    SCC.resize(N);
    O.resize(N);
    calcReverseEdges();
    vector<bool> V(N, false);
    vector<int> F(N, 0);
    int t = 0;
    for (int i = N - 1; i >= 0; i--) {
      if (!V[i]) {
        dfsReverse(i, V, F, t);
      }
      O[N - 1 - F[i]] = i;
    }
    fill(V.begin(), V.end(), false);
    for (int i = 0; i < N; i++) {
      if (!V[O[i]]) dfsForward(O[i], O[i], V);
    }
  }
  void dfsForward(int i, int p, vector<bool>& V) {
    V[i] = true;
    SCC[i] = p;
    for (int v : E[i])
      if (!V[v]) dfsForward(v, p, V);
  }
  void dfsReverse(int i, vector<bool>& V, vector<int>& F, int& t) {
    V[i] = true;
    for (int v : ER[i]) {
      if (!V[v]) dfsReverse(v, V, F, t);
    }
    F[i] = t++;
  }
  int N;
  vector<vector<int>> E, ER;
  vector<int> SCC, O;
};
struct TwoSat {
  int N;
  vector<int> val;
  vector<char> valid;
  vector<vector<int>> G;
  TwoSat(int N) : N(N), val(2 * N), G(2 * N) {}
  void addImplication(int a, bool w, int b, bool first) {
    G[2 * a + w].push_back(2 * b + first);
  }
  void addOr(int a, bool w, int b, bool first) {
    addImplication(a, !w, b, first);
    addImplication(b, !first, a, w);
  }
  void addTrue(int first) { addImplication(first, false, first, true); }
  void addFalse(int first) { addImplication(first, true, first, false); }
  int time() { return valid.size() - 1; }
  bool dfs(int first) {
    if (valid[abs(val[first])]) return val[first] > 0;
    val[first] = time();
    val[first ^ 1] = -time();
    for (int e : G[first])
      if (!dfs(e)) return false;
    return true;
  }
  bool value(int i) const { return val[2 * i] < 0; }
  bool solve() {
    fill(val.begin(), val.end(), 0);
    valid.assign(1, 0);
    for (int i = 0; i < val.size(); i += 2) {
      if (!valid[abs(val[i])]) {
        valid.push_back(1);
        if (!dfs(i)) {
          valid.back() = 0;
          valid.push_back(1);
          if (!dfs(i + 1)) return false;
        }
      }
    }
    return true;
  }
};
constexpr int ATTEMPTS = 100;
class E {
 public:
  int N, M;
  vector<vector<int>> E;
  vector<bool> Visited, OnStack;
  vector<int> Stack, Up;
  vector<int> StackId;
  vector<bool> Bad;
  vector<vector<int>> UpGraph;
  AddMaxTree<int> TT;
  bool isInteresting(int v) {
    if (Visited[v]) return OnStack[v];
    Visited[v] = true;
    OnStack[v] = true;
    Stack.push_back(v);
    for (int w : E[v]) {
      if (!isInteresting(w)) return false;
    }
    Stack.pop_back();
    OnStack[v] = false;
    return true;
  }
  void dfs(int v) {
    if (Visited[v]) {
      TT.put(StackId[v], Stack.size() - 2, 1);
      return;
    }
    Visited[v] = true;
    OnStack[v] = true;
    StackId[v] = Stack.size();
    Stack.push_back(v);
    for (int w : E[v]) {
      bool visited = Visited[w];
      dfs(w);
      if (visited) {
        UpGraph[w].push_back(v);
      } else {
        int val = TT.get(StackId[v]);
        if (val >= 2)
          Bad[w] = true;
        else
          UpGraph[w].push_back(v);
        if (val) TT.put(StackId[v], -val);
      }
    }
    Stack.pop_back();
    OnStack[v] = false;
  }
  void propagateUp(int i) {
    if (!Bad[i]) return;
    for (int j : UpGraph[i]) {
      if (!Bad[j]) {
        Bad[j] = true;
        propagateUp(j);
      }
    }
  }
  void solve(istream& cin, ostream& cout) {
    int T;
    cin >> T;
    for (int t = 0; t < T; ++t) {
      cin >> N >> M;
      E.clear();
      E.resize(N);
      for (int i = 0; i < M; ++i) {
        int a, b;
        cin >> a >> b;
        --a;
        --b;
        E[a].push_back(b);
      }
      if (N <= 20) {
        vector<int> Good;
        for (int i = 0; i < N; ++i) {
          Stack.clear();
          Visited.assign(N, false);
          OnStack.assign(N, false);
          if (isInteresting(i)) {
            Good.push_back(i + 1);
          }
        }
        if (Good.size() * 5 < N) {
          cout << "-1\n";
        } else {
          cout << Good;
        }
        continue;
      }
      int interesting = -1;
      random_int R(0, N - 1);
      for (int att = 0; att < ATTEMPTS; ++att) {
        int start = R(rng);
        Stack.clear();
        Visited.assign(N, false);
        OnStack.assign(N, false);
        if (isInteresting(start) &&
            count(Visited.begin(), Visited.end(), false) == 0) {
          interesting = start;
          break;
        }
      }
      if (interesting == -1) {
        cout << "-1\n";
        continue;
      }
      Stack.clear();
      StackId.resize(N);
      Visited.assign(N, false);
      OnStack.assign(N, false);
      TT.setup(N, 0);
      Bad.assign(N, false);
      UpGraph.clear();
      UpGraph.resize(N);
      dfs(interesting);
      DirectedGraph DG(N);
      for (int i = 0; i < N; ++i) {
        for (int j : E[i]) {
          DG.addEdge(i, j);
        }
      }
      DG.calcStronglyConnectedComponents();
      vector<int> Prop;
      for (int i = 0; i < N; ++i)
        if (Bad[i]) Prop.push_back(i);
      for (int p : Prop) propagateUp(p);
      vector<int> Good;
      for (int i = 0; i < N; ++i) {
        if (!DG.stronglyConnected(i, interesting)) {
          Bad[i] = true;
        }
        if (!Bad[i]) Good.push_back(i + 1);
      }
      if (Good.size() * 5 < N) {
        cout << "-1\n";
      } else {
        cout << Good;
      }
    }
  }
};
int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);
  cout.tie(nullptr);
  E solver;
  std::istream& in(std::cin);
  std::ostream& out(std::cout);
  solver.solve(in, out);
  return 0;
}
