#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int Max=10086,Inf=(1<<30);
inline int MIN(int a,int b)
{
return a<b?a:b;
}
inline int MAX(int a,int b)
{
return a>b?a:b;
}
int val[Max];
struct SegmentTree
{
int maxv[Max<<2],minv[Max<<2];
bool flag[Max<<2];
inline void PushUp(int idx)
{
maxv[idx]=MAX(maxv[idx<<1],maxv[idx<<1|1]);
minv[idx]=MIN(minv[idx<<1],minv[idx<<1|1]);
}
inline void PushDown(int idx)
{
if(flag[idx])
{
int l=idx<<1,r=l+1;
maxv[l]=-maxv[l],minv[l]=-minv[l],swap(maxv[l],minv[l]);
maxv[r]=-maxv[r],minv[r]=-minv[r],swap(maxv[r],minv[r]);
flag[l]^=1,flag[r]^=1;
flag[idx]=false;
}
}
void Build(int idx,int left,int right)
{
flag[idx]=false;
if(left==right)
{
maxv[idx]=minv[idx]=val[left];
return;
}
int mid=(left+right)>>1;
Build(idx<<1,left,mid);
Build(idx<<1|1,mid+1,right);
PushUp(idx);
}
void Update(int idx,int left,int right,int pos,int data)
{
if(left==right)
{
maxv[idx]=minv[idx]=data;
return;
}
PushDown(idx);
int mid=(left+right)>>1;
if(pos<=mid) Update(idx<<1,left,mid,pos,data);
else Update(idx<<1|1,mid+1,right,pos,data);
PushUp(idx);
}
void Negate(int idx,int left,int right,int L,int R)
{
if(L<=left && right<=R)
{
maxv[idx]=-maxv[idx],minv[idx]=-minv[idx];
swap(maxv[idx],minv[idx]);
flag[idx]^=1;
return;
}
PushDown(idx);
int mid=(left+right)>>1;
if(L<=mid) Negate(idx<<1,left,mid,L,R);
if(mid<R) Negate(idx<<1|1,mid+1,right,L,R);
PushUp(idx);
}
int Query(int idx,int left,int right,int L,int R)
{
if(L<=left && right<=R)
return maxv[idx];
PushDown(idx);
int mid=(left+right)>>1,ans=-Inf;
if(L<=mid) ans=MAX(ans,Query(idx<<1,left,mid,L,R));
if(mid<R) ans=MAX(ans,Query(idx<<1|1,mid+1,right,L,R));
return ans;
}
}tree;
int f[Max],fa[Max],tot[Max],son[Max],top[Max],pos[Max],depth[Max],size;
struct Edge
{
int v,w,next;
}edge[Max<<1];
void Add(int u,int v,int w,int& cnt)
{
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=f[u];
f[u]=cnt++;
edge[cnt].v=u;
edge[cnt].w=w;
edge[cnt].next=f[v];
f[v]=cnt++;
}
void Init()
{
memset(f,-1,sizeof(f));
top[1]=fa[1]=1,size=depth[1]=0;
}
void Dfs1(int cur)
{
tot[cur]=1,son[cur]=0;
int v,maxv=-1;
for(int i=f[cur];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(v!=fa[cur])
{
fa[v]=cur,depth[v]=depth[cur]+1;
Dfs1(v);
tot[cur]+=tot[v];
if(tot[v]>maxv) maxv=tot[v],son[cur]=v;
}
}
}
void Dfs2(int cur)
{
int v=son[cur],i;
pos[cur]=++size;
if(v) top[v]=top[cur],Dfs2(v);
for(i=f[cur];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(v==fa[cur]) val[pos[cur]]=edge[i].w;
else if(v!=son[cur]) top[v]=v,Dfs2(v);
}
}
inline void Update(int idx,int data)
{
int i=idx*2-1;
int u=edge[i].v,v=edge[i^1].v;
if(depth[u]<depth[v]) swap(u,v);
tree.Update(1,2,size,pos[u],data);
}
void Negate(int u,int v)
{
int topu=top[u];
int topv=top[v];
while(topu!=topv)
{
if(depth[topu]<depth[topv]) swap(u,v),swap(topu,topv);
tree.Negate(1,2,size,pos[topu],pos[u]);
u=fa[topu],topu=top[u];
}
if(u==v) return ;
if(depth[u]>depth[v]) swap(u,v);
tree.Negate(1,2,size,pos[son[u]],pos[v]);
}
int Query(int u,int v)
{
if(u==v) return 0;
int topu=top[u];
int topv=top[v];
int ans=-Inf;
while(topu!=topv)
{
if(depth[topu]<depth[topv]) swap(u,v),swap(topu,topv);
ans=MAX(ans,tree.Query(1,2,size,pos[topu],pos[u]));
u=fa[topu],topu=top[u];
}
if(u==v) return ans;
if(depth[u]>depth[v]) swap(u,v);
ans=MAX(ans,tree.Query(1,2,size,pos[son[u]],pos[v]));
return ans;
}
int main()
{
//freopen("test.txt","r",stdin);
int N,T,u,v,cnt,i,w;
char str[22];
cin>>T;
while(T--)
{
scanf("%d",&N);
Init();
for(i=1,cnt=0;i<N;++i)
{
scanf("%d %d %d",&u,&v,&w);
Add(u,v,w,cnt);
}
Dfs1(1),Dfs2(1),tree.Build(1,2,N);
while(scanf("%s",str),str[0]!='D')
{
scanf("%d %d",&u,&v);
if(str[0]=='C') Update(u,v);
else if(str[0]=='N') Negate(u,v);
else printf("%d\n",Query(u,v));
}
}
return 0;
}