#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
#define pb push_back
#define pii pair<int, int>
#define pll pair<ll, ll>
#define st first
#define nd second
#define ordered_set tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update>
#define debug false
struct kraw {
int v;
int c;
int i;
};
const int MAXN = 1000 * 1000 + 17;
const int MAXM = 1000 * 1000 + 17;
vector<kraw> graf[MAXN];
map<int, vector<kraw>> jakie[MAXN];
bool odw[MAXM];
bool wdfs[MAXM];
int ojc[MAXM];
kraw tkraw[MAXM];
vector<int> wyn;
bool jest = false;
int p, k;
void DFS (kraw a) {
auto it = jakie[a.v].begin();
while (it != jakie[a.v].end()) {
if (it->first == a.c) {
it ++;
continue;
}
vector<kraw> vec = it->second;
while (!vec.empty()) {
kraw b = vec.back();
vec.pop_back();
if (!odw[b.i]) {
odw[b.i] = true;
wdfs[b.i] = true;
ojc[b.i] = a.i;
DFS(b);
if (jest) {
return;
}
} else if (wdfs[b.i]) {
jest = true;
p = a.i;
k = b.i;
return;
}
}
it = jakie[a.v].erase(it);
}
wdfs[a.i] = false;
}
void czysc (int n, int m) {
for (int i = 1; i <= n; i ++) {
graf[i].clear();
jakie[i].clear();
}
for (int i = 0; i < m; i ++) {
odw[i] = false;
ojc[i] = 0;
wdfs[i] = false;
}
wyn.clear();
}
void rozw () {
int n, m;
cin >> n >> m;
int a, b, c;
for (int i = 0; i < m; i ++) {
cin >> a >> b >> c;
graf[a].pb({b, c, i});
tkraw[i] = {b, c, i};
}
for (int i = 1; i <= n; i ++) {
for (auto x : graf[i]) {
jakie[i][x.c].pb(x);
}
}
jest = false;
for (int i = 0; i < m; i ++) {
if (!odw[i] && !jest) {
odw[i] = true;
wdfs[i] = true;
DFS(tkraw[i]);
}
}
if (jest) {
cout << "YES\n";
wyn.pb(k);
while (p != k) {
wyn.pb(p);
p = ojc[p];
}
reverse(wyn.begin(), wyn.end());
cout << int(wyn.size()) << " ";
for (auto x : wyn) {
cout << x + 1 << " ";
}
cout << "\n";
} else {
cout << "NO\n";
}
czysc(n, m);
}
int main () {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t;
cin >> t;
while (t --) {
rozw();
}
return 0;
}